Codeforces Round 223 380C Sereja and Brackets 树状数组

类似的题目有好多,总结成下面的模型:

一个序列,有tot组点对,固定不变,m次查询[l,r]这个区间上能够完全覆盖多少组点对。

由于这些点对是固定不变的,不支持任何的插入,修改,删除,所以我们采取离线操作。

我们建两颗树状数组来处理这个查询的问题。

tr1[i]表示有多少点对的小的点在[0,i]之间;tr2[i]表示有多少点对的大的点在[0,i]之间;

那下面这六种点对代表了对于查询[l,r]的所有点对;

tr1[l-1]=1+4+6;

tr2[r]=1+2+4;

如果我们是按照点对的长度来依次将点对加入到树状数组中去的话,那么每次对于查询[l,r],我们就将所有长度小于r-l+1的加入到树状数组中去,就避免了6这种点对的计算;

tr1[l-1]=1+4;

tr2[r]=1+2+4;




那我们可以得到

ans=tr2[r]-tr1[l-1];

这样总体的时间复杂度大概是O(nlogn);


#include <iostream>
#include <stdio.h>
#include <string.h>
#include <algorithm>

using namespace std;
const int maxn=1100000;
int n,m,tot;
char s[1100000];
int tr1[maxn],tr2[maxn];
struct q
{
    int l,r,num,ans;
}qu[maxn];
struct node
{
    int l,r;
}a[maxn];
int d[1100000];
bool cmp1(q a,q b)
{
    return (a.r-a.l<b.r-b.l);
}
bool cmp3(node a,node b)
{
    return (a.r-a.l<b.r-b.l);
}
bool cmp2(q a,q b)
{
    return (a.num<b.num);
}
int low(int p)
{
    return p&(-p);
}
void insert(int tr[],int p)
{
    if (p==0)
        {tr[0]++;
    return ;}
    while (p<maxn)
    {
        tr[p]++;
        p+=low(p);
    }
}
int find(int tr[],int p)
{
    int ans=tr[0];
    while (p)
    {
        ans+=tr[p];
        p-=low(p);
    }
    return ans;
}
void work()
{
    //上面有解释;
    memset(tr1,0,sizeof(tr1));
    memset(tr2,0,sizeof(tr2));
    int k2=0;
    for (int k1=0;k1<m;k1++)
    {
        while (a[k2].r-a[k2].l<=qu[k1].r-qu[k1].l&&k2<tot)
        {
            insert(tr1,a[k2].l);
            insert(tr2,a[k2].r);
            k2++;
        }
        qu[k1].ans=find(tr2,qu[k1].r)-find(tr1,qu[k1].l-1);
    }
}
void pre()
{
    //对于这个题目,就是找相匹配的括号在字符串上的位置点对;
    //用栈操作,找出匹配的字符串;
    int pop=1;
    int l=strlen(s);
    tot=0;
    for (int i=0;i<l;i++)
    if (s[i]=='(')
    {
        d[pop]=i+1;
        pop++;
    }
    else
    {
        if (pop>1)
        {
            pop--;
            a[tot].l=d[pop];
            a[tot].r=i+1;//将匹配的括号的对应的位置加入到a中;
            tot++;
        }
    }
}
int main()
{
   // freopen("in.txt","r",stdin);
    while (~scanf("%s",s))
    {
        pre();//做前期的处理工作,将数据转换为区间上的点对;
        sort(a,a+tot,cmp3);//对a进行区间长度由小到大的排序;
        scanf("%d",&m);
        for (int i=0;i<m;i++)
        {
            scanf("%d%d",&qu[i].l,&qu[i].r);//读入查询;
            qu[i].num=i;
        }
        sort(qu,qu+m,cmp1);//对查询进行区间长度由小到大的排序;
        work();//用树状数组离线处理;
        sort(qu,qu+m,cmp2);//对查询进行原始排序;
        for (int i=0;i<m;i++)
        printf("%d\n",qu[i].ans*2);
    }
    return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值