【模板】莫队

普通莫队
例题

#include<bits/stdc++.h>
using namespace std;
const int M=5e4+5;
long long e,ans;
void read(long long &x) {
	int f=1;x=0;char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
	while(c>='0'&&c<='9') {x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	x*=f;
}
struct node{
	long long id,l,r;
};
node k[M];
long long n,a[M],q,cnt[M],block,Ans[M];
bool cmp(node a,node b){
	return (a.l/block)^(b.l/block)?(a.l/block)<(b.l/block):(a.l/block)&1?a.r<b.r:a.r>b.r;
}
long long b[M];
void add(int x){
	cnt[a[x]]++;
	ans+=2*cnt[a[x]]-1;
}
void del(int x){
	cnt[a[x]]--;
	ans-=2*cnt[a[x]]+1;
}
int main(){
	read(n);read(q);read(e);
	for(int i=1;i<=n;i++){
		read(a[i]);
	}
	block=n/sqrt(q*2/3);
	for(int i=1;i<=q;i++){
		read(k[i].l);read(k[i].r);
		k[i].id=i;
	}
	sort(k+1,k+q+1,cmp);
	int LL=1,RR=0;
	for(int i=1;i<=q;i++){
		int ll=k[i].l,rr=k[i].r;
		while(LL<ll){
			del(LL++);
		}
		while(RR>rr){
			del(RR--);
		}
		while(ll<LL){
			add(--LL);
		}
		while(rr>RR){
			add(++RR);
		}
		Ans[k[i].id]=ans;
	}
	for(int i=1;i<=q;i++){
		printf("%lld\n",Ans[i]);
	}
	return 0;
}
 

带修莫队
就是在普通莫队的基础上加了一维表示修改的次数。
例题

#include<bits/stdc++.h>
using namespace std;
const int M=3e6+5;
long long e,ans,tn,tot,t;
void read(long long &x) {
	int f=1;x=0;char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
	while(c>='0'&&c<='9') {x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	x*=f;
}
struct node{
	long long id,l,r,t;
};
node k[M];
struct nod{long long pla,pre,suc;}cg[M];
long long n,a[M],q,cnt[M],block,Ans[M];
bool cmp(node a,node b){
	if((a.l/block)!=(b.l/block))
	return (a.l/block)<(b.l/block);
	if((a.r/block)!=(b.r/block))
	return (a.r/block)<(b.r/block);
	return a.t<b.t;
}
long long b[M];
int main(){
	read(n);read(q);
	block=pow(n,(double)2/(double)3);
	for(int i=1;i<=n;i++){
		read(a[i]);
	}
	char s[5];
	for(int i=1;i<=q;i++){
		scanf("%s",s);
		if(s[0]=='R'){
			++tn;read(cg[tn].pla);read(cg[tn].suc);
			cg[tn].pre=a[cg[tn].pla];
			a[cg[tn].pla]=cg[tn].suc;
		}
		else {
			++tot;
			read(k[tot].l);read(k[tot].r);
			k[tot].id=tot;
			k[tot].t=tn;
		}		
	}
	for(int i=tn;i>=1;i--) a[cg[i].pla]=cg[i].pre;
	sort(k+1,k+tot+1,cmp);
	int LL=1,RR=0;
	for(int i=1;i<=q;i++){
		int ll=k[i].l,rr=k[i].r;
		while(LL<ll){
			ans-=!--cnt[a[LL++]];
		}
		while(RR>rr){
			ans-=!--cnt[a[RR--]];
		}
		while(ll<LL){
			ans+=!cnt[a[--LL]]++;
		}
		while(rr>RR){
			ans+=!cnt[a[++RR]]++;
		}
		while(k[i].t<t){
			int pla=cg[t].pla;
			if(LL<=pla&&pla<=RR) ans-=!--cnt[a[pla]];
			a[pla]=cg[t--].pre;
			if(LL<=pla&&pla<=RR) ans+=!cnt[a[pla]]++;
		}
		while(k[i].t>t){
			int pla=cg[++t].pla;
			if(LL<=pla&&pla<=RR) ans-=!--cnt[a[pla]];
			a[pla]=cg[t].suc;
			if(LL<=pla&&pla<=RR) ans+=!cnt[a[pla]]++;
		}
		Ans[k[i].id]=ans;
	}
	for(int i=1;i<=tot;i++){
		printf("%lld\n",Ans[i]);
	}
	return 0;
}
 

树上莫队
把树上的一条路径转化为了欧拉序中的一个区间,分是否为LCA的两种情况进行讨论。
例题

#include<bits/stdc++.h>
using namespace std;
const int M=1e5+5;
long long ans;
void read(long long &x) {
	int f=1;x=0;char c=getchar();
	while(c<'0'||c>'9') {if(c=='-') f=-1;c=getchar();}
	while(c>='0'&&c<='9') {x=(x<<1)+(x<<3)+(c^48);c=getchar();}
	x*=f;
}
struct node{
	long long id,l,r,x;
};
node k[M];
vector<int> G[M];
long long n,a[M],q,_cnt[M],block,Ans[M],d[M],f[M][25],cnt,e[M],s[M],t[M],vis[M],lsh[M];
void dfs(int x){
	d[x]=d[f[x][0]]+1;
	e[++cnt]=x,s[x]=cnt;
	for(int i=1;i<20;i++) f[x][i]=f[f[x][i-1]][i-1];
	int siz=G[x].size();
	for(int i=0;i<siz;i++){
		int y=G[x][i];
		if(y==f[x][0]) continue;
		f[y][0]=x;
		dfs(y);
	}
	e[++cnt]=x;t[x]=cnt;
}
int LCA(int x,int y){
	if(d[x]>d[y]) swap(x,y);
	for(int i=20;i>=0;i--){
		if(d[f[y][i]]>=d[x]) {		
			y=f[y][i];
		}
	}
	if(x==y) {
		return x;
	}
	for(int i=20;i>=0;i--){
		if(f[x][i]!=f[y][i]) {
			x=f[x][i];y=f[y][i];
		}
	}
	return f[x][0];
}
bool cmp(node a,node b){
	return (a.l/block)==(b.l/block)?a.r>b.r:(a.l/block)<(b.l/block);
}
long long b[M];
void upd(int x){
	if(vis[x]==0&&_cnt[a[x]]++==0) ans++;
	if(vis[x]==1&&--_cnt[a[x]]==0) ans--;
	vis[x]^=1;
}
int main(){
	read(n);read(q);
	for(int i=1;i<=n;i++){
		read(a[i]);
		lsh[i]=a[i];
	}
	sort(lsh+1,lsh+n+1);
    for(int i=1;i<=n;i++) a[i]=lower_bound(lsh+1,lsh+n+1,a[i])-lsh; 
    for(int i=1,x,y;i<n;i++){
    	scanf("%d %d",&x,&y);
    	G[x].push_back(y);
    	G[y].push_back(x);
	}
	dfs(1);
	block=n/sqrt(q*2/3);
	for(int i=1;i<=q;i++){
		long long x,y;
		read(x);read(y);
		int lca=LCA(x,y);
		if(x==lca||y==lca){
			if(s[x]>s[y]) swap(x,y);
			k[i].l=s[x];k[i].r=s[y];
		}
		else {
			if(t[y]<s[x]) swap(x,y);
			k[i].l=t[x];k[i].r=s[y];k[i].x=lca;
		}
		k[i].id=i;
	}
	sort(k+1,k+q+1,cmp);
	int LL=1,RR=0;
	for(int i=1;i<=q;i++){
		int ll=k[i].l,rr=k[i].r;
		while(LL<ll){
			upd(e[LL++]);
		}
		while(RR>rr){
			upd(e[RR--]);
		}
		while(ll<LL){
			upd(e[--LL]);
		}
		while(rr>RR){
			upd(e[++RR]);
		}
		if(k[i].x){
			upd(k[i].x);
		}
		Ans[k[i].id]=ans;
		if(k[i].x){
			upd(k[i].x);
		}
	}
	for(int i=1;i<=q;i++){
		printf("%lld\n",Ans[i]);
	}
	return 0;
}
 

树上莫队带修
例题
其实就是把树上莫队和带修莫队合起来,但是有点难打。

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int M=1e5+5;
int n,m,q,uu,vv,c[M],opt,l,r,tt;
ll v[M],w[M],ans[M],sum;
struct node{
	int to,next;
}e[M<<1];
int tot,head[M<<1];
void add(int u,int v){
	e[++tot].to=v;
	e[tot].next=head[u];
	head[u]=tot;
}
int in[M],out[M],kk;
int back[M<<1],deep[M],f[M];
int fa[M][21],vis[M];
void dfs(int u,int ff){
	f[u]=ff;
	in[u]=++kk;back[kk]=u;
	for(int i=head[u];i;i=e[i].next){
		int v=e[i].to;
		if(v==ff) continue;
		dfs(v,u);
	}
	out[u]=++kk;back[kk]=u;
}
void dfslca(int u){
	vis[u]=1;
	for(int i=1;(1<<i)<=deep[u];i++){
		fa[u][i]=fa[fa[u][i-1]][i-1];
	}
	for(int i=head[u];i;i=e[i].next){
		int v=e[i].to;
		if(vis[v]) continue;
		deep[v]=deep[u]+1;
		dfslca(v);
	}
}
int bl,t[M],flag[M],qq,rr;
struct ques{
	int u,v,lca,l,r,id,t;
}qu[M];
struct point{
	int pos,candy;
}re[M];
bool cmp(ques a,ques b){
	if(a.l/bl==b.l/bl){
		if(a.r/bl==b.r/bl) return a.t<b.t;
		return a.r<b.r;
	}
	return a.l/bl<b.l/bl;
}
int LCA(int x,int y){
	if(deep[x]<deep[y])
	swap(x,y);
	int h=deep[x]-deep[y];
	for(int i=0;(1<<i)<=h;i++){
		if((1<<i)&h) x=fa[x][i];
	}
	if(x==y) return x;
	for(int i=20;i>=0;i--){
		if(fa[x][i]!=fa[y][i]){
			x=fa[x][i];
			y=fa[y][i];
		}
	}
	return fa[x][0];
}
void change(int x){
	if(flag[re[x].pos]==1){
		t[c[re[x].pos]]--;
		sum-=w[t[c[re[x].pos]]+1]*v[c[re[x].pos]];
		t[re[x].candy]++;
		sum+=w[t[re[x].candy]]*v[re[x].candy];
	}
	swap(re[x].candy,c[re[x].pos]);
}

int main(){
	scanf("%d %d %d",&n,&m,&q);
	for(int i=1;i<=m;i++) scanf("%lld",&v[i]);
	for(int i=1;i<=n;i++) scanf("%lld",&w[i]);
	for(int i=1;i<=n-1;i++){
		scanf("%d %d",&uu,&vv);
		add(uu,vv);
		add(vv,uu);
	}
	for(int i=1;i<=n;i++) scanf("%d",&c[i]);
	dfs(1,1);
	for(int i=1;i<=n;i++) fa[i][0]=f[i];
	dfslca(1);
	bl=pow(n,(double)2/(double)3);
	for(int i=1;i<=q;i++){
		scanf("%d",&opt);
		scanf("%d %d",&uu,&vv);
		if(opt==0) {
			re[++rr].pos=uu;
			re[rr].candy=vv;
		}
		if(opt==1){
			qu[++qq].lca=LCA(uu,vv);
			qu[qq].id=qq;
			qu[qq].t=rr;
			qu[qq].u=uu;qu[qq].v=vv;
		
			if(in[qu[qq].u]>in[qu[qq].v])
			swap(qu[qq].u,qu[qq].v);
			if(qu[qq].lca==qu[qq].u){
				qu[qq].l=in[qu[qq].u];
				qu[qq].r=in[qu[qq].v];
				qu[qq].lca=0;
			}
			else{
				qu[qq].l=out[qu[qq].u];
				qu[qq].r=in[qu[qq].v];
			}
		}
	}
	sort(qu+1,qu+qq+1,cmp);
	for(int i=1;i<=qq;i++){
		while(l<qu[i].l){
			flag[back[l]]--;
			if(flag[back[l]]==1) t[c[back[l]]]++;
			if(flag[back[l]]==0) t[c[back[l]]]--;
			if(flag[back[l]]==0)
			sum-=w[t[c[back[l]]]+1]*v[c[back[l]]];
			if(flag[back[l]]==1)
			sum+=w[t[c[back[l]]]]*v[c[back[l]]];
			l++;
		}
		while(l>qu[i].l){
			flag[back[--l]]++;
			if(flag[back[l]]==1) t[c[back[l]]]++;
			if(flag[back[l]]==2) t[c[back[l]]]--;
			if(flag[back[l]]==2)
			sum-=w[t[c[back[l]]]+1]*v[c[back[l]]];
			if(flag[back[l]]==1)
			sum+=w[t[c[back[l]]]]*v[c[back[l]]];
		}
		while(r<qu[i].r){
			flag[back[++r]]++;
			if(flag[back[r]]==1) t[c[back[r]]]++;
			if(flag[back[r]]==2) t[c[back[r]]]--;
			if(flag[back[r]]==2)
			sum-=w[t[c[back[r]]]+1]*v[c[back[r]]];
			if(flag[back[r]]==1)
			sum+=w[t[c[back[r]]]]*v[c[back[r]]];
		}
		while(r>qu[i].r){
			flag[back[r]]--;
			if(flag[back[r]]==1) t[c[back[r]]]++;
			if(flag[back[r]]==0) t[c[back[r]]]--;
			if(flag[back[r]]==0)
			sum-=w[t[c[back[r]]]+1]*v[c[back[r]]];
			if(flag[back[r]]==1)
			sum+=w[t[c[back[r]]]]*v[c[back[r]]];
			r--;
		}
		while(tt<qu[i].t) change(++tt);
		while(tt>qu[i].t) change(tt--);
		if(qu[i].lca){
			t[c[qu[i].lca]]++;
			sum+=w[t[c[qu[i].lca]]]*v[c[qu[i].lca]];
		}
		ans[qu[i].id]=sum;
		if(qu[i].lca){
			t[c[qu[i].lca]]--;
			sum-=w[t[c[qu[i].lca]]+1]*v[c[qu[i].lca]];
		}
	}
	for(int i=1;i<=qq;i++)
	printf("%lld\n",ans[i]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值