询问某区间内有多少满足某个要求的/某区间以某式子计算出的值(莫队算法,不修改只询问区间)

我觉得能用线段树就用线段树吧,实在用不了了再来考虑莫队算法,比如下面两道题:

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;  
}  

你可能感兴趣的:(询问某区间内有多少满足某个要求的/某区间以某式子计算出的值(莫队算法,不修改只询问区间))