普通莫队
例题
#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;
}