类似的题目有好多,总结成下面的模型:
一个序列,有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;
}