Codeforces 381C Sereja and Brackets【二分+思维】好题~

C. Sereja and Brackets
time limit per test
1 second
memory limit per test
256 megabytes
input
standard input
output
standard output

Sereja has a bracket sequence s1, s2, ..., sn, or, in other words, a string s of length n, consisting of characters "(" and ")".

Sereja needs to answer m queries, each of them is described by two integers li, ri (1 ≤ li ≤ ri ≤ n). The answer to the i-th query is the length of the maximum correct bracket subsequence of sequence sli, sli + 1, ..., sri. Help Sereja answer all queries.

You can find the definitions for a subsequence and a correct bracket sequence in the notes.

Input

The first line contains a sequence of characters s1, s2, ..., sn (1 ≤ n ≤ 106) without any spaces. Each character is either a "(" or a ")". The second line contains integer m (1 ≤ m ≤ 105) — the number of queries. Each of the next m lines contains a pair of integers. The i-th line contains integers li, ri (1 ≤ li ≤ ri ≤ n) — the description of the i-th query.

Output

Print the answer to each question on a single line. Print the answers in the order they go in the input.

Examples
Input
())(())(())(
7
1 1
2 3
1 2
1 12
8 12
5 11
2 10
Output
0
0
2
10
4
6
6
Note

A subsequence of length |x| of string s = s1s2... s|s| (where |s| is the length of string s) is string x = sk1sk2... sk|x| (1 ≤ k1 < k2 < ... < k|x| ≤ |s|).

A correct bracket sequence is a bracket sequence that can be transformed into a correct aryphmetic expression by inserting characters "1" and "+" between the characters of the string. For example, bracket sequences "()()", "(())" are correct (the resulting expressions "(1)+(1)", "((1+1)+1)"), and ")(" and "(" are not.

For the third query required sequence will be «()».

For the fourth query required sequence will be «()(())(())».


题目大意:

一共有两种操作:

①1 x,在数组最后端加一个数x。

②2 x,y 重复数组中前x个数y次加在数组最后端。

求最后N个询问位子上的数。保证输入都是合法的。


思路:


1、首先观察到,对应第二种操作只会重复数组前100000位子上的数,那么我们首先预处理:

①暴力预处理出前100000位子上的数,记录到ans【i】中。

②维护一个数组sum【i】表示一直进行操作到第i次,整个数组现在的长度。


2、对应每次查询,考虑到随着操作次数的增多,数组的整个长度一定在增大这个单调性,那么我们考虑对长度进行二分。

对应二分过程中有很多细节(列举不全大家的地方参考代码):

对应当前枚举的二分值mid:

①如果sum【mid-1】<=x&&sum【mid】>=x,那么对应我们处理解:

如果a【mid】.op==1(操作1),那么对应output=a【mid】.x;

如果a【mid】.op==2(操作2),那么对应output=ans【(x-sum[mid-1])%a[mid].x】;

(只是我代码挫写的不好的附加产物的样纸----------------->)并且我们记录当前解,注意这里不能记录一次解之后就break掉,因为有可能出现连续的两个操作1的情况,会出现偏差,那么对应我们r=mid-1,记录最小的位子mid时候的解.

②对应如果不是上述情况,如果此时sum【mid】>x,那么对应r=mid-1,否则l=mid+1.

对应一直二分下去,每次有可行解的时候都进行记录,输出最后的可行解。


3、因为数据范围都不小,会有爆int的地方,图方便,我全部将int替换成了LL,各位强迫症看官不要打我啊QWQ


Ac代码(代码君:我是挫逼但我很萌!):

#include<stdio.h>
#include<string.h>
using namespace std;
#define ll __int64
struct node
{
    ll op;
    ll x,y;
}a[1000500];
ll m;
ll sum[1050000];
ll ans[1020000];
void init()
{
    for(ll i=0;i<m;i++)
    {
        ll sumpre;
        if(i==0)sumpre=0;
        else sumpre=sum[i-1];
        if(a[i].op==1)sum[i]=1+sumpre;
        else
        {
            ll tmp=(ll)a[i].x;
            ll tmp2=(ll)a[i].y;
            sum[i]=sumpre+tmp*tmp2;
        }
    }
    ll now=1;
    for(ll i=0;i<m;i++)
    {
        if(a[i].op==1)
        {
            ans[now++]=a[i].x;
            if(now>100050)break;
        }
        else
        {
            for(ll j=0;j<a[i].y;j++)
            {
                for(ll k=1;k<=a[i].x;k++)
                {
                    ans[now++]=ans[k];
                    if(now>100050)break;
                }
                if(now>100050)break;
            }
        }
    }
}
int main()
{
    while(~scanf("%I64d",&m))
    {
        memset(sum,0,sizeof(sum));
        memset(ans,-1,sizeof(ans));
        for(ll i=0;i<m;i++)
        {
            scanf("%I64d",&a[i].op);
            if(a[i].op==1)
            {
                scanf("%I64d",&a[i].x);
            }
            if(a[i].op==2)
            {
                scanf("%I64d%I64d",&a[i].x,&a[i].y);
            }
        }
        init();
        ll q;
        scanf("%I64d",&q);
        while(q--)
        {
            ll x;
            scanf("%I64d",&x);
            ll l=0;
            ll r=m-1;
            ll output;
            while(r-l>=0)
            {
                ll mid=(l+r)/2;
                if(x==sum[mid]||(mid-1>=0&&x<=sum[mid]&&x>=sum[mid-1]))
                {
                    if(a[mid].op==1)
                    {
                        output=a[mid].x;
                        r=mid-1;
                    }
                    else
                    {
                        ll pos=(x-sum[mid-1])%a[mid].x;
                        if(pos==0)pos=a[mid].x;
                        output=ans[pos];
                        r=mid-1;
                    }
                    continue;
                }
                if(x<sum[mid])
                {
                    r=mid-1;
                }
                else l=mid+1;
            }
            printf("%I64d ",output);
        }
        printf("\n");
    }
}
/*
4
1 1
1 2
2 2 100000
2
*/




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值