您的位置:首页 > 其它

分块_莫队算法

2014-05-08 23:59 176 查看
题意:
给定N个数,记为ai(1<=i<= N),求区间内某两个数和为给定的M的对数。一共Q次询问
数据范围:
T<=20
N<= 20000,M <= 20000
1 <= ai<M
Q<= 20000,1<= l < r <= n
现在知道[l,r]的答案ans,可以O(1)得出[l,r+1]的答案
c[i]表示现在值为i的数有几个,那么a[r+1]=x对答案贡献了c[M-x],所以[l,r+1]的答案就是ans+c[M-x]
同理可以O(1)得到[l-1,r]的答案
voidinsert(int x){
x=a[x];
ans+=c[M-x];
++c[x];
}
现在知道[l,r]的答案ans,可以O(1)得出[l,r-1]的答案
假设a[r]=x,要分两种情况讨论:
1)x+x!=M, 答案是 ans-c[M-x]
2)x+x==M,答案是ans-(c[M-x]-1)
同理可以O(1)得到[l+1,r]的答案
void erase(int x){
x=a[x];
ans-=c[M-x]-(x+x==M);
--c[x];
}
现在知道[p,q]的答案,现在求[l,r]的答案

考虑右端点:
1)如果q<r,把a[q+1],a[q+2]…a[r]插入
2)如果q>r,把a[r+1],a[r+2]…a[q]删除
时间复杂度是|q-r|

考虑左端点:
1)如果p<l,把a[p],a[p+1]…a[l]删除
2)如果p>l,把a[l+1],a[l+2]…a[p]插入
时间复杂度是|p-l|

这个过程时间复杂度就是|p-l|+|q-r|

while (q<r) insert(++q);
while (q>r) erase(q--);
while (p<l) erase(p++);
while (p>l) insert(--p);
1)读入所有的询问,并记下是第几个询问
2)把N个数分成sqrt(N)块,算出每个询问的左端点在哪个块 pos=l/sqrt(N)
3)把询问以pos为第一关键字,r为第二关键字排序
4)按新的顺序求出所有询问的答案
struct Query{
int id,l,r,pos;
bool operator<(const Query &t)const{
return pos!=t.pos?pos<t.pos:r<t.r;
}
};
Query b[21000];
sort(b+1,b+Q+1); // 把b[1],b[2]...b[Q]排序
int main(){
int _;
scanf("%d",&_);
while (_--){
scanf("%d%d",&N,&M);
int i;
for (i=1;i<=N;i++) scanf("%d",&a[i]);
scanf("%d",&Q);
for (i=1;i<=Q;i++){
b[i].id=i;
scanf("%d%d",&b[i].l,&b[i].r);
b[i].pos=b[i].l/sqrt(N);
}
sort(b+1,b+Q+1);
memset(c,0,sizeof(c));
ans=0;
int p=1,q=1; insert(1);
for (i=1;i<=Q;i++){
int l=b[i].l,r=b[i].r;
while (q<r) insert(++q);
while (q>r) erase(q--);
while (p<l) erase(p++);
while (p>l) insert(--p);
t[b[i].id]=ans;
}
for (i=1;i<=Q;i++) printf("%I64d\n",t[i]);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: