CF380C Sereja and Brackets [想法+线段树]

题意:

给出一串括号

给出一些询问,问某个区间[l,r]内的能合法匹配的括号数有多少个

分析:

我们可以实现处理两个数组

sum[i] 1....i中已经能匹配的右括号的数目

left[i] 1....i中还不能匹配的左括号数目

这两个数组可以很简单的扫描一遍动态维护得出来

我们可以先求前缀和,即

1...m中有多少能匹配的右括号sum[m]

则,我们可以得到sum[r]-sum[l-1],区间[l,r]内能合法匹配的右括号

但是这些右括号的匹配包括了和[1,l-1]中的左括号的匹配

所以我们再减去left[l-1],但是

这又会多减去了[1,l-1]中和[r+1....]右括号匹配的左括号

所以得加上这部分

这部分是多少?这是这个问题的关键也是这个问题比较难以理解的地方

这部分是min(left[l]...left[r])
为什么?

…...(..(.......
.....)......(............ ..)........)

1 2        L  3     4           R 5       6

红色部分是询问区间L,R

可以看出

我们这里减去了1,2括号

然而括号1是不应该减去的,它和括号6配对

括号2是应该减去的,它和括号3配对

如果我们取L,R上left的最小值,那么可以看到最小值落在括号3和括号4之间,可以避免把括号4算入(它和括号5匹配)

而且也可以把多减掉的括号1给加上

然而这里还有一个tricks,如果,类似4号括号的这样的括号在L,R区间上第一个位置的时候,这里是没有min的位置的。

判断这种情况也确实麻烦

我们作如下处理

如果 left[l-1]-min(left[l]...left[r])<0则不减,否则减去(因为L,R内能匹配的右括号不超过sum[r]-sum[l-1],后者中的右括号还能和1,L上的左括号匹配)

#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <cmath>
#include <algorithm>
#include <iostream>
#include <vector>
#include <queue>
#define L(x) x<<1
#define R(x) x<<1|1
using namespace std;
const int NN=1111111;
char str[NN];
char tmp[NN];
int l[NN],r[NN];
struct TREE{
	int x;
}tr[NN*6];
void build(int idx,int L,int R){
	if(L==R){
		tr[idx].x=l[L];
		return;
	}
	int mid=(L+R)/2;
	build(L(idx),L,mid);
	build(R(idx),mid+1,R);
	tr[idx].x=min(tr[L(idx)].x,tr[R(idx)].x);
}
int que(int idx,int ll,int rr,int L,int R){
	int mid=(L+R)/2;
	if(ll==L && rr==R){
        return tr[idx].x;
	}
	if(rr<=mid){
		return que(L(idx),ll,rr,L,mid);
	}else if(ll>=mid+1){
		return que(R(idx),ll,rr,mid+1,R);
	}else{
		return min(que(L(idx),ll,mid,L,mid),que(R(idx),mid+1,rr,mid+1,R));
	}
}
int main(){
#ifndef ONLINE_JUDGE
	freopen("G:/in.txt","r",stdin);
	//freopen("G:/myout.txt","w",stdout);
#endif
	cin>>(str+1);
	//cin>>tmp;
	int n;cin>>n;
	int len=strlen(str+1);
	for(int i=1;i<=len;i++){
		l[i]=l[i-1];
		r[i]=r[i-1];
		if(str[i]=='('){
			l[i]++;
		}else{
			if(l[i]){
				l[i]--;
				r[i]++;
			}
		}
	}
	build(1,1,len);
	while(n--){
		int x,y;cin>>x>>y;
		if(x==y){
            cout<<0<<endl;
            continue;
		}
		int ans=r[y]-r[x-1];
		ans-=max(0,l[x-1]-que(1,x,y,1,len));
		cout<<ans*2<<endl;
	}
}
时间: 2024-10-08 15:35:48

CF380C Sereja and Brackets [想法+线段树]的相关文章

Codeforces 380C. Sereja and Brackets【线段树】

题目大意: 给出一串括号,有m个查询(包含a,b)问区间[a,b]之间有多少个匹配的括号 做法: 处理两个数组r[i](代表从1到i之间有多少个已匹配的右括号),l[i](代表从1到i之间有多少个没有匹配的左括号).我们要算[a,b]之间的匹配的括号数,首先用r[b]-r[a-1],但是遮掩更有可能a到b之间的右括号是在[1,a-1]之间被匹配的,那么再减去l[a-1],但是减去的这一部分中,也有可能是在[b+1,len]这个区间中被匹配,要加上这一部分不该减去的数值,那么只需要在加上min(l

CF380C Sereja and Brackets 括号序列+线段树

你可以手画一下,然后发现求的其实就是 $[l,r]$ 区间内合法序列匹配个数. 用线段树维护一下括号序列就可以了. code: #include <bits/stdc++.h> #define N 1000005 #define ll long long #define lson now<<1 #define rson now<<1|1 #define setIO(s) freopen(s".in","r",stdin) usin

CodeForces 380C Sereja and Brackets(扫描线+树状数组)

[题目链接] http://codeforces.com/problemset/problem/380/C [题目大意] 给出一个括号序列,求区间内左右括号匹配的个数. [题解] 我们发现对于每个右括号,其匹配的左括号是固定的, 我们保存每个右括号匹配的左括号位置, 对区间询问进行线扫描,将扫描的区间右端点及其之前所有的右括号对应的左括号位置做标记, 只要查询询问区间的标记个数就是答案,这个可以用树状数组维护. [代码] #include <cstdio> #include <algor

Sereja and Array-数组操作或者线段树或树状数组

CodeForces - 315B Sereja and Array Time Limit: 1000MS   Memory Limit: 262144KB   64bit IO Format: %I64d & %I64u Submit Status Description Sereja has got an array, consisting of n integers, a1,?a2,?...,?an. Sereja is an active boy, so he is now going

ZJOI2017 day2 T2 线段树 想法题

考完D2发现自己简直zz了...花式扔基本分 首先这道题有个显然的套路:树上一些点到一个定点的距离和=这些点深度和+点数*定点深度和-2*lca深度和 --上一次见这个套路是LNOI2014,上次做的时候还比较naive:http://www.cnblogs.com/wanglichao/p/6425893.html 这次考场上也只想到这一步了,,并没有发现广义线段树的奇特性质 奇特性质:被选中的从左到右一定是一串右儿子和一串左儿子,而且都是挂在l-1到r+1上的连续右(左)儿子 这么一来,一个

POJ 2828 线段树(想法)

Buy Tickets Time Limit: 4000MS   Memory Limit: 65536K Total Submissions: 15422   Accepted: 7684 Description Railway tickets were difficult to buy around the Lunar New Year in China, so we must get up early and join a long queue… The Lunar New Year wa

线段树(二)

转自:http://blog.csdn.net/liujian20150808/article/details/51137749 1.线段树的定义: 线段树是一种二叉搜索树,与区间树相似,它将一个区间划分成一些单元区间,每个单元区间对应线段树中的一个叶结点. 对于线段树中的每一个非叶子节点[a,b],它的左儿子表示的区间为[a,(a+b)/2],右儿子表示的区间为[(a+b)/2+1,b].因此线段树是平衡二叉树,最后的子节点数目为N,即整个线段区间的长度. 举例描述: 因此有了以上对线段树的定

BZOJ 2725: [Violet 6]故乡的梦 最短路+线段树

2725: [Violet 6]故乡的梦 Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 678  Solved: 204[Submit][Status][Discuss] Description Input Output Sample Input 6 7 1 2 1 2 3 1 3 4 2 4 5 1 5 6 1 1 3 3 4 6 3 1 6 4 1 2 1 3 4 3 6 5 Sample Output 7 6 Infinity 7 HINT

hdu 5195 线段树

题目描述:给定一个DAG,求出允许移除最多K条边后的字典序最大的拓扑序列. 思路:线段树,每次找入度不超过K的最大编号的顶点,将此顶点从图中移除,重复操作n次即可得到结果. 吐槽:当时打BC的时候写出了一个直接贪心+拓扑排序的复杂度为O(n)的错误代码(当时还没有意识到是错误代码),交到hdu oj上居然给过了,后来上西方文化的时候和csc得瑟,说那个题我300+ms就给过了,在best solutions里面Rank 1,复杂度还是O(n)的,然后和csc说了我的想法以后才发现这思路TM根本就