我觉得能用线段树就用线段树吧,实在用不了了再来考虑莫队算法,比如下面两道题:
http://codeforces.com/contest/617/problem/E (询问某区间内有多少个子区间的异或值是K)
题解:http://blog.csdn.net/huayunhualuo/article/details/50585720
#include<bits/stdc++.h> #define LL long long using namespace std; const int Max = 1100000; const int MAXM = 1<<22; typedef struct node { int L ,R; int Id; }Point ; Point a[Max]; LL sum[Max]; LL ans[Max]; int n,m; LL k; int L,R; LL cnt[MAXM],ant; bool cmp(Point b,Point c)//将区间分块排序 { if(b.L/400==c.L/400) { return b.R<c.R; } else { return b.L<c.L; } } void Dec(LL s) //将多算的数目去除 { --cnt[s]; ant-=cnt[s^k]; } void Inc(LL s)//将没有遍历的点对应的数目加上 { ant += cnt[s^k]; cnt[s]++; } int main() { scanf("%d %d %lld",&n,&m,&k); LL data; for(int i=1;i<=n;i++) { scanf("%lld",&sum[i]); sum[i]^=sum[i-1]; } for(int i=1;i<=m;i++) { scanf("%d %d",&a[i].L,&a[i].R); a[i].Id = i; a[i].L--;// 在这里提前处理 } sort(a+1,a+m+1,cmp); L=0,R=0,cnt[0]=1,ant=0; for(int i=1;i<=m;i++) { while(R<a[i].R) { R++; Inc(sum[R]); } while(R>a[i].R) { Dec(sum[R]); R--; } while(L<a[i].L) { Dec(sum[L]); L++; } while(L>a[i].L) { --L; Inc(sum[L]); } ans[a[i].Id]=ant; } for(int i=1;i<=m;i++) { printf("%lld\n",ans[i]); } return 0; }
http://www.lydsy.com/JudgeOnline/problem.php?id=2038 (询问某区间内取到一对值相等的数的概率)
#include<bits/stdc++.h> #define LL long long using namespace std; const int Max = 50010; const int MAXM = 50010; typedef struct node { int L ,R; int Id; }Point ; Point a[Max]; LL sum[Max]; pair<LL,LL> ans[Max]; int n,m; int unit; LL k; int L,R; LL cnt[MAXM],ant; bool cmp(Point b,Point c)//将区间分块排序 { if(b.L/unit==c.L/unit) { return b.R<c.R; } else { return b.L<c.L; } } void Dec(LL s) //将多算的数目去除 { --cnt[s]; ant-=cnt[s]; } void Inc(LL s)//将没有遍历的点对应的数目加上 { ant+=cnt[s]; cnt[s]++; } int main() { scanf("%d %d",&n,&m); unit=sqrt(n*1.0); for(int i=1;i<=n;i++) { scanf("%lld",&sum[i]); //如果是求连续的某段区间的话sum最好弄成前缀和 } for(int i=1;i<=m;i++) { scanf("%d %d",&a[i].L,&a[i].R); a[i].Id = i; // a[i].L--;// 在这里提前处理(作差值时才需要) } sort(a+1,a+m+1,cmp); L=R=0,cnt[0]=1,ant=0; for(int i=1;i<=m;i++) { while(R<a[i].R) { R++; Inc(sum[R]); } while(R>a[i].R) { Dec(sum[R]); R--; } while(L<a[i].L) { Dec(sum[L]); L++; } while(L>a[i].L) { --L; Inc(sum[L]); } LL ss=(LL)(a[i].R-a[i].L+1)*(a[i].R-a[i].L)/2; //注意转化LL LL aa=__gcd(ant,ss); ans[a[i].Id].first=ant/aa; ans[a[i].Id].second=(ant==0?1:ss/aa); } for(int i=1;i<=m;i++) { printf("%lld/%lld\n",ans[i].first,ans[i].second); } return 0; }
http://codeforces.com/problemset/problem/86/D(询问某区间内所有出现过的数字*(它出现过的次数)^2之和)http://www.cnblogs.com/riskyer/archive/2013/07/29/3223621.html
#include<bits/stdc++.h> #define LL long long using namespace std; const int Max = 2000005; const int MAXM = 1000005; typedef struct node { int L ,R; int Id; }Point ; Point a[Max]; LL sum[Max]; LL ans[Max]; int n,m; LL k; int L,R; LL cnt[MAXM],ant; bool cmp(Point b,Point c)//将区间分块排序 { if(b.L/400==c.L/400) { return b.R<c.R; } else { return b.L<c.L; } } void Dec(LL s) //将多算的数目去除 { --cnt[s]; ant-=((cnt[s]<<1)+1)*s; } void Inc(LL s)//将没有遍历的点对应的数目加上 { ant+=((cnt[s]<<1)+1)*s; cnt[s]++; } int main() { scanf("%d %d",&n,&m); LL data; for(int i=1;i<=n;i++) { scanf("%I64d",&sum[i]); //如果是求连续的某段区间的话sum最好弄成前缀和 } for(int i=1;i<=m;i++) { scanf("%d %d",&a[i].L,&a[i].R); a[i].Id = i; // a[i].L--;// 在这里提前处理(作差值时才需要) } sort(a+1,a+m+1,cmp); L=R=0,cnt[0]=1,ant=0; for(int i=1;i<=m;i++) { while(R<a[i].R) { R++; Inc(sum[R]); } while(R>a[i].R) { Dec(sum[R]); R--; } while(L<a[i].L) { Dec(sum[L]); L++; } while(L>a[i].L) { --L; Inc(sum[L]); } ans[a[i].Id]=ant; } for(int i=1;i<=m;i++) { printf("%I64d\n",ans[i]); } return 0; }
#define LL long long using namespace std; const int Max = 2000005; const int MAXM = 1000005; typedef struct node { int L ,R; int Id; }Point ; Point a[Max]; LL sum[Max]; LL ans[Max]; int n,m; LL k; int L,R; LL cnt[MAXM],ant; bool cmp(Point b,Point c)//将区间分块排序 { if(b.L/(int) sqrt(n)!=c.L/(int) sqrt(n)) { return b.L<c.L; } else { return b.R<c.R; } } int main() { scanf("%d %d",&n,&m); for(int i=1;i<=n;i++) { scanf("%I64d",&sum[i]); } for(int i=1;i<=m;i++) { scanf("%d %d",&a[i].L,&a[i].R); a[i].Id = i; // a[i].L--;// 在这里提前处理(作差值时才需要) } sort(a+1,a+m+1,cmp); L=0,R=0,cnt[0]=0,ant=0; for(int i=1;i<=m;i++) { while(L < a[i].L) { ant -= sum[L] * (2 * cnt[sum[L]]-- - 1); L++; } while(R > a[i].R) { ant -= sum[R] * (2 * cnt[sum[R]]-- - 1); R--; } while(L > a[i].L) { L--; ant += sum[L] * (2 * cnt[sum[L]]++ + 1); } while(R < a[i].R) { R++; ant += sum[R] * (2 * cnt[sum[R]]++ + 1); } ans[a[i].Id]=ant; } for(int i=1;i<=m;i++) { printf("%I64d\n",ans[i]); } return 0; }
这两种情况都不似区间求和,能够通过线段树直接求出来。这时考虑莫队算法http://www.tuicool.com/articles/mYzQZzF
此系列:http://www.2cto.com/kf/201502/376381.html
必须先排序。
【杭电5213】
每组数据给你n(1<=n<=30000)个数字,每个数字都在[1,n]之间。
并且有m(1<=m<=30000)个询问。
还告诉你一个数字K(2<=k<=2n且k为奇数)。
对于第i个询问,给你2个区间,
[l1~r1] [l2~r2],数据保证1<=l1<=r1<l2<=r2<=n
让你求出有多少对pair(a[x],a[y]),使得——
a[x]在[l1,r1],a[y]在[l2,r2]且a[x]+a[y]==K.
【分析】
这道题设计到区间询问,而且可以离线处理。于是我们很自然地想到莫队算法。
我们发现数字的范围很小,于是我们可以直接计数1~n的数分别是多少个。
然后因为K为奇数,所以就自然不会需要考虑一个数和自己自成pair。
这道题有一个需要处理的问题,就是一般的莫队是只有一个区间,而这道题却有两个区间,该怎么办?
于是我们还需要——容斥。 (转)
#include<iostream> #include<algorithm> #include<string> #include<map>//int dx[4]={0,0,-1,1};int dy[4]={-1,1,0,0}; #include<set>//int gcd(int a,int b){return b?gcd(b,a%b):a;} #include<vector> #include<cmath> #include<stack> #include<string.h> #include<stdlib.h> #include<cstdio> #define LL long long using namespace std; const int Max = 30010; const int MAXM = 30010; typedef struct node { int L ,R; int Id; }Point ; Point a[Max*4]; LL sum[Max]; LL ans[Max*4]; int n,m; int unit; int k; // int L,R; LL cnt[MAXM],ant; bool cmp(Point b,Point c)//将区间分块排序 { if(b.L/unit==c.L/unit) { return b.R<c.R; } else { return b.L<c.L; } } void Dec(LL s) //将多算的数目去除 { --cnt[s]; if(k-s>=1&&k-s<=n) ant-=cnt[k-s]; } void Inc(LL s)//将没有遍历的点对应的数目加上 { if(k-s>=1&&k-s<=n) ant+=cnt[k-s]; cnt[s]++; } int main() { while(scanf("%d %d",&n,&k)==2){ unit=sqrt(n*1.0); for(int i=1;i<=n;i++) { scanf("%lld",&sum[i]); //如果是求连续的某段区间的话sum最好弄成前缀和 } scanf("%d",&m); for(int i=1;i<=m*4;i+=4) { scanf("%d %d",&a[i].L,&a[i].R); a[i].Id = i; scanf("%d %d",&a[i+1].L,&a[i+1].R); a[i+1].Id = i+1; a[i+2].L=a[i].R+1; a[i+2].R=a[i+1].L-1; a[i+2].Id = i+2; a[i+3].L=a[i].L; a[i+3].R=a[i+1].R; a[i+3].Id = i+3; a[i].R=a[i+2].R; a[i+1].L=a[i+2].L; // a[i].L--;// 在这里提前处理(作差值时才需要) } sort(a+1,a+m*4+1,cmp); L=R=0,cnt[0]=1,ant=0; for(int i=1;i<=m*4;i++) { if(a[i].R<=a[i].L){ ans[a[i].Id]=0; continue; } while(R<a[i].R) { R++; Inc(sum[R]); } while(R>a[i].R) { Dec(sum[R]); R--; } while(L<a[i].L) { Dec(sum[L]); L++; } while(L>a[i].L) { --L; Inc(sum[L]); } ans[a[i].Id]=ant; } for(int i=1;i<=m*4;i+=4) { //[l1,r2]-[l1,l2-1]-[r1+1,r2]+[r1+1,l2-1] printf("%lld\n",ans[i+3]-ans[i]-ans[i+1]+ans[i+2]); } } return 0; }