我觉得能用线段树就用线段树吧,实在用不了了再来考虑莫队算法,比如下面两道题:
http://codeforces.com/contest/617/problem/E (询问某区间内有多少个子区间的异或值是K)
题解:http://blog.youkuaiyun.com/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;
}