P4887 第十四分块(前体)

P4887 第十四分块(前体)

链接

P4887 第十四分块(前体)

题解

二次离线莫队。。新技能get。。
用于可以使用莫队且单次转移的效率不是 (O(1)) 的情况。
对于每次指针移动,先不直接算,而是把询问差分然后再次离线询问。
这样可以算出每一问对于上一个询问的答案的变化量,最后再重新按末对的顺序加起来就可以。

(Code)

#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int N=1e5+10;
int read(){
    int x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void print(LL x){
    if(x>9) print(x/10);
    putchar(x%10+'0');
}

int n,m,K,B; 
int a[N],be[N];
struct query{
	int l,r,id;
}q[N];
bool cmp(query x,query y){
	if(be[x.l]!=be[y.l]) return be[x.l]<be[y.l];
	return x.r<y.r;
}
//pre[x][0]=f(x,[1,x-1]) pre[x][1]=f(x,[1,x])
LL pre[N][2],s[N];
LL ans[N];
vector<int> c;
struct P{
	int l,r,id,f;
	P(int lx=0,int rx=0,int idx=0,int fx=0){
		l=lx;r=rx;id=idx;f=fx;
	}
};
vector<P> ve[N];
int main(){
	scanf("%d%d%d",&n,&m,&K);B=sqrt(n);
	for(int i=1;i<=n;++i) {
		scanf("%d",&a[i]);
		be[i]=(i+B-1)/B;
	}
	for(int i=1;i<=m;++i){
		scanf("%d%d",&q[i].l,&q[i].r);q[i].id=i;
	}
	sort(q+1,q+1+m,cmp);
	for(int i=0;i<16384;++i) 
		if(__builtin_popcount(i)==K) c.push_back(i);
	for(int i=1;i<=n;++i){
		for(int j=0;j<c.size();++j) ++s[a[i]^c[j]];
		pre[i+1][0]=s[a[i+1]];pre[i][1]=s[a[i]];
	}
	memset(s,0,sizeof(s));
	int l=1,r=0;
	for(int i=1;i<=m;++i){
		if(r<q[i].r) ve[l-1].push_back(P(r+1,q[i].r,q[i].id,-1));
		while(r<q[i].r){++r;ans[q[i].id]+=pre[r][0];} 
		//r++ f(x,[l,x-1])=f(x,[1,x-1])-f(x,[1,l-1])
		if(l>q[i].l) ve[r].push_back(P(q[i].l,l-1,q[i].id,1));
		while(l>q[i].l){--l;ans[q[i].id]-=pre[l][1];}
		//l-- f(x,[x+1,r])=f(x,[1,r])-f(x,[1,x])
		if(r>q[i].r) ve[l-1].push_back(P(q[i].r+1,r,q[i].id,1));
		while(r>q[i].r){ans[q[i].id]-=pre[r][0];--r;} 
		//r-- -f(x,[l,x-1])=-f(x,[1,x-1])+f(x,[1,l-1])
		if(l<q[i].l) ve[r].push_back(P(l,q[i].l-1,q[i].id,-1));
		while(l<q[i].l){ans[q[i].id]+=pre[l][1];++l;}
		//l++ -f(x,[x+1,r])=-f(x,[1,r])+f(x,[1,x])
	}
	for(int i=1;i<=n;++i){
		for(int j=0;j<c.size();++j) ++s[a[i]^c[j]];
		for(int j=0;j<ve[i].size();++j){
			for(int k=ve[i][j].l;k<=ve[i][j].r;++k){
				ans[ve[i][j].id]+=(LL)ve[i][j].f*s[a[k]];
			}
		}
	}
	for(int i=2;i<=m;++i){
		ans[q[i].id]+=ans[q[i-1].id]; 
	}
	for(int i=1;i<=m;++i) {
		print(ans[i]);
		puts("");
	}
	return 0;
}
原文地址:https://www.cnblogs.com/Yuigahama/p/13519215.html