[學習筆記]樹上莫隊
阿新 • • 發佈:2018-11-26
其實樹上莫隊是一個尤拉序而已嘛,像普通的莫隊,特判一下出現過兩次的值就行了
設 \(st_i\) 為 \(i\) 進棧的時間,\(ed_i\) 為 \(i\) 出棧的時間,\(dfn_x<dfn_y\) ,那麼就可以分兩種情況:
1、\(y\) 在 \(x\) 子樹中,也就是 \(LCA(x,y)=x\),那麼區間轉化成 \([st_x,st_y]\)
2、\(y\) 不在 \(x\) 子樹中,也就是 \(LCA(x,y)\not =x\),那麼區間轉化成 \([ed_x,st_y]\)
然後就是板子了
for(int i=1;i<=m;i++){ x=read(),y=read(); q[i].anc=LCA(x,y);q[i].id=i; if(x==q[i].anc||y==q[i].anc){ q[i].anc=0;q[i].l=st[x];q[i].r=st[y]; if(q[i].l>q[i].r) q[i].l=st[y],q[i].r=st[x]; } else { q[i].l=ed[x];q[i].r=st[y]; if(q[i].l>q[i].r) q[i].l=ed[y],q[i].r=st[x]; } }
1、SP10707 COT2 - Count on a tree II
其實我國慶的時候就學了,只不過我一直 \(WA\)...後來從倍增換成樹剖,重構一遍程式碼就 \(A\) 了
\(Code\ Below:\)
#include <bits/stdc++.h> using namespace std; const int maxn=100000+10; int n,m,a[maxn],mp[maxn],vis[maxn],cnt[maxn],ans[maxn],st[maxn],ed[maxn],p[maxn<<1],tim,blo,now; int head[maxn],to[maxn<<1],nxt[maxn<<1],tot; int top[maxn],dep[maxn],siz[maxn],son[maxn],fa[maxn]; struct Query{ int l,r,anc,id; }q[maxn]; bool cmp(Query a,Query b){ if((a.l-1)/blo!=(b.l-1)/blo) return (a.l-1)/blo<(b.l-1)/blo; return a.r<b.r; } inline int read(){ register int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();} return (f==1)?x:-x; } inline void add(int x,int y){ to[++tot]=y; nxt[tot]=head[x]; head[x]=tot; } void dfs1(int x,int f){ siz[x]=1;fa[x]=f; dep[x]=dep[f]+1; st[x]=++tim;p[tim]=x; int maxson=-1; for(int i=head[x],y;i;i=nxt[i]){ y=to[i]; if(y==f) continue; dfs1(y,x); siz[x]+=siz[y]; if(maxson<siz[y]){ maxson=siz[y]; son[x]=y; } } ed[x]=++tim;p[tim]=x; } void dfs2(int x,int topf){ top[x]=topf; if(son[x]) dfs2(son[x],topf); for(int i=head[x],y;i;i=nxt[i]){ y=to[i]; if(y==fa[x]||y==son[x]) continue; dfs2(y,y); } } int LCA(int x,int y){ while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]]) swap(x,y); x=fa[top[x]]; } if(dep[x]>dep[y]) swap(x,y); return x; } int update(int x){ if(vis[x]){ if(--cnt[a[x]]==0) now--; } else if(++cnt[a[x]]==1) now++; vis[x]^=1; } int main() { n=read(),m=read();blo=sqrt(n); for(int i=1;i<=n;i++) mp[i]=a[i]=read(); sort(mp+1,mp+n+1); int t=unique(mp+1,mp+n+1)-mp-1; for(int i=1;i<=n;i++) a[i]=lower_bound(mp+1,mp+t+1,a[i])-mp; int x,y; for(int i=1;i<n;i++){ x=read(),y=read(); add(x,y);add(y,x); } dfs1(1,0);dfs2(1,1); for(int i=1;i<=m;i++){ x=read(),y=read(); q[i].anc=LCA(x,y);q[i].id=i; if(x==q[i].anc||y==q[i].anc){ q[i].anc=0;q[i].l=st[x];q[i].r=st[y]; if(q[i].l>q[i].r) q[i].l=st[y],q[i].r=st[x]; } else { q[i].l=ed[x];q[i].r=st[y]; if(q[i].l>q[i].r) q[i].l=ed[y],q[i].r=st[x]; } } sort(q+1,q+m+1,cmp); int L=1,R=0; for(int i=1;i<=m;i++){ while(R<q[i].r) update(p[++R]); while(R>q[i].r) update(p[R--]); while(L<q[i].l) update(p[L++]); while(L>q[i].l) update(p[--L]); if(q[i].anc) update(q[i].anc); ans[q[i].id]=now; if(q[i].anc) update(q[i].anc); } for(int i=1;i<=m;i++) printf("%d\n",ans[i]); return 0; }
2、蘋果樹(本地Cena自測)
其實就是特判一下 \(cnt_a\) 和 \(cnt_b\)
只不過我非常傻,離散了一下顏色,就 \(GG\) 了
\(Code\ Below:\)
#include <cstdio> #include <cctype> #include <cmath> #include <algorithm> using namespace std; const int maxn=100000+10; int n,m,a[maxn],mp[maxn],vis[maxn],cnt[maxn],ans[maxn],st[maxn],ed[maxn],p[maxn<<1],tim,blo,now; int head[maxn],to[maxn<<1],nxt[maxn<<1],tot; int top[maxn],dep[maxn],siz[maxn],son[maxn],fa[maxn]; struct Query{ int l,r,a,b,anc,id; }q[maxn]; bool cmp(Query a,Query b){ if((a.l-1)/blo!=(b.l-1)/blo) return (a.l-1)/blo<(b.l-1)/blo; return a.r<b.r; } inline int read(){ register int x=0,f=1;char ch=getchar(); while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();} while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();} return (f==1)?x:-x; } inline void add(int x,int y){ to[++tot]=y; nxt[tot]=head[x]; head[x]=tot; } void dfs1(int x,int f){ siz[x]=1;fa[x]=f; dep[x]=dep[f]+1; st[x]=++tim;p[tim]=x; int maxson=-1; for(int i=head[x],y;i;i=nxt[i]){ y=to[i]; if(y==f) continue; dfs1(y,x); siz[x]+=siz[y]; if(maxson<siz[y]){ maxson=siz[y]; son[x]=y; } } ed[x]=++tim;p[tim]=x; } void dfs2(int x,int topf){ top[x]=topf; if(son[x]) dfs2(son[x],topf); for(int i=head[x],y;i;i=nxt[i]){ y=to[i]; if(y==fa[x]||y==son[x]) continue; dfs2(y,y); } } int LCA(int x,int y){ while(top[x]!=top[y]){ if(dep[top[x]]<dep[top[y]]) swap(x,y); x=fa[top[x]]; } if(dep[x]>dep[y]) swap(x,y); return x; } void update(int x){ if(vis[x]){ if(--cnt[a[x]]==0) now--; } else if(++cnt[a[x]]==1) now++; vis[x]^=1; } int main() { n=read(),m=read();blo=sqrt(2*n); for(int i=1;i<=n;i++) a[i]=read(); int x,y; for(int i=1;i<=n;i++){ x=read(),y=read(); if(x&&y) add(x,y),add(y,x); } dfs1(1,0);dfs2(1,1); for(int i=1;i<=m;i++){ x=read(),y=read(),q[i].a=read(),q[i].b=read(); q[i].anc=LCA(x,y);q[i].id=i; if(x==q[i].anc||y==q[i].anc){ q[i].anc=0;q[i].l=st[x];q[i].r=st[y]; if(q[i].l>q[i].r) q[i].l=st[y],q[i].r=st[x]; } else { q[i].l=ed[x];q[i].r=st[y]; if(q[i].l>q[i].r) q[i].l=ed[y],q[i].r=st[x]; } } sort(q+1,q+m+1,cmp); int L=1,R=0; for(int i=1;i<=m;i++){ while(R<q[i].r) update(p[++R]); while(R>q[i].r) update(p[R--]); while(L<q[i].l) update(p[L++]); while(L>q[i].l) update(p[--L]); if(q[i].anc) update(q[i].anc); ans[q[i].id]=now; if(q[i].a!=q[i].b&&cnt[q[i].a]&&cnt[q[i].b]) ans[q[i].id]--; if(q[i].anc) update(q[i].anc); } for(int i=1;i<=m;i++) printf("%d\n",ans[i]); return 0; }
3、Haruna’s Breakfast
小知識:帶修莫隊塊大小在 \(n^{\frac 23}\) 跑的最快……不過我不會證
本人錯誤點:莫隊修改過來的時候應該先將修改的時間戳 \(+1\),再更新;莫隊修改回去的時候應該先更新,再將修改的時間戳 \(-1\)
\(Code\ Below:\)
#include <bits/stdc++.h>
using namespace std;
const int maxn=100000+10;
int n,m,a[maxn],mp[maxn],pos[maxn],val[maxn],vis[maxn],cnt[maxn],ans[maxn],st[maxn],ed[maxn],p[maxn<<1],tim,blo,now,Cnum,Qnum,an;
int head[maxn],to[maxn<<1],nxt[maxn<<1],tot;
int top[maxn],dep[maxn],siz[maxn],son[maxn],fa[maxn];
struct Query{
int l,r,anc,pre,id;
}q[maxn];
bool cmp(Query a,Query b){
if((a.l-1)/blo!=(b.l-1)/blo)
return (a.l-1)/blo<(b.l-1)/blo;
if(a.r!=b.r) return a.r<b.r;
return a.pre<b.pre;
}
inline int read(){
register int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return (f==1)?x:-x;
}
inline void add(int x,int y){
to[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
void dfs1(int x,int f){
siz[x]=1;fa[x]=f;
dep[x]=dep[f]+1;
st[x]=++tim;p[tim]=x;
int maxson=-1;
for(int i=head[x],y;i;i=nxt[i]){
y=to[i];
if(y==f) continue;
dfs1(y,x);
siz[x]+=siz[y];
if(maxson<siz[y]){
maxson=siz[y];
son[x]=y;
}
}
ed[x]=++tim;p[tim]=x;
}
void dfs2(int x,int topf){
top[x]=topf;
if(son[x]) dfs2(son[x],topf);
for(int i=head[x],y;i;i=nxt[i]){
y=to[i];
if(y==fa[x]||y==son[x]) continue;
dfs2(y,y);
}
}
int LCA(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
return x;
}
inline void update(int x){
if(a[x]<maxn){
if(vis[x]){
if(--cnt[a[x]]==0) now=min(now,a[x]);
}
else if(++cnt[a[x]]==1) while(cnt[now]) now++;
}
vis[x]^=1;
}
inline void Del(int x){if(vis[x]&&a[x]<maxn) if(--cnt[a[x]]==0) now=min(now,a[x]);}
inline void Add(int x){if(vis[x]&&a[x]<maxn) if(++cnt[a[x]]==1) while(cnt[now]) now++;}
int main()
{
n=read(),m=read();blo=pow(2*n,0.666666);
for(int i=1;i<=n;i++) a[i]=read();
int opt,x,y;
for(int i=1;i<n;i++){
x=read(),y=read();
add(x,y);add(y,x);
}
dfs1(1,0);dfs2(1,1);
for(int i=1;i<=m;i++){
opt=read(),x=read(),y=read();
if(opt==0){
pos[++Qnum]=x;val[Qnum]=y;
}
else {
q[++Cnum].anc=LCA(x,y);q[Cnum].pre=Qnum;q[Cnum].id=Cnum;
if(x==q[Cnum].anc||y==q[Cnum].anc){
q[Cnum].anc=0;q[Cnum].l=st[x];q[Cnum].r=st[y];
if(q[Cnum].l>q[Cnum].r) q[Cnum].l=st[y],q[Cnum].r=st[x];
}
else {
q[Cnum].l=ed[x];q[Cnum].r=st[y];
if(q[Cnum].l>q[Cnum].r) q[Cnum].l=ed[y],q[Cnum].r=st[x];
}
}
}
sort(q+1,q+Cnum+1,cmp);
int L=1,R=0;
for(int i=1;i<=Cnum;i++){
while(R<q[i].r) update(p[++R]);
while(R>q[i].r) update(p[R--]);
while(L<q[i].l) update(p[L++]);
while(L>q[i].l) update(p[--L]);
if(q[i].anc) update(q[i].anc);
while(an<q[i].pre){
an++;
Del(pos[an]);
swap(a[pos[an]],val[an]);
Add(pos[an]);
}
while(an>q[i].pre){
Del(pos[an]);
swap(a[pos[an]],val[an]);
Add(pos[an]);
an--;
}
ans[q[i].id]=now;
if(q[i].anc) update(q[i].anc);
}
for(int i=1;i<=Cnum;i++) printf("%d\n",ans[i]);
return 0;
}
4、GT and trees
綜合題,求樹上路徑待修改區間眾數,不難想到帶修樹上莫隊
然後就再記錄一個 \(f[i]\) 表示區間出現 \(i\) 次的數的個數,\(add\) 沒什麼好說的,\(del\) 就暴力找,跟 \(mex\) 一樣
\(Code\ Below:\)
#include <cstdio>
#include <cctype>
#include <cmath>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn=100000+10;
int n,m,a[maxn],vis[maxn],f[maxn],cnt[maxn],pos[maxn],val[maxn];
int Ans[maxn],st[maxn],ed[maxn],p[maxn<<1],tim,blo,Cnum,Qnum,ans,now;
int head[maxn],to[maxn<<1],nxt[maxn<<1],tot;
int top[maxn],dep[maxn],siz[maxn],son[maxn],fa[maxn];
struct Query{
int l,r,anc,pre,id;
}q[maxn];
bool cmp(Query a,Query b){
if((a.l-1)/blo!=(b.l-1)/blo)
return (a.l-1)/blo<(b.l-1)/blo;
if(a.r!=b.r) return a.r<b.r;
return a.pre<b.pre;
}
inline int read(){
register int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return (f==1)?x:-x;
}
inline void add(int x,int y){
to[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
void dfs1(int x,int f){
siz[x]=1;fa[x]=f;
dep[x]=dep[f]+1;
st[x]=++tim;p[tim]=x;
int maxson=-1;
for(int i=head[x],y;i;i=nxt[i]){
y=to[i];
if(y==f) continue;
dfs1(y,x);
siz[x]+=siz[y];
if(maxson<siz[y]){
maxson=siz[y];
son[x]=y;
}
}
ed[x]=++tim;p[tim]=x;
}
void dfs2(int x,int topf){
top[x]=topf;
if(son[x]) dfs2(son[x],topf);
for(int i=head[x],y;i;i=nxt[i]){
y=to[i];
if(y==fa[x]||y==son[x]) continue;
dfs2(y,y);
}
}
int LCA(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
return x;
}
inline void update(int x){
if(vis[x]){
if(--f[cnt[a[x]]--]==0) while(!f[ans]) ans--;
}
else if(++f[++cnt[a[x]]]==1) ans=max(ans,cnt[a[x]]);
vis[x]^=1;
}
inline void Add(int x){if(vis[x]) if(++f[++cnt[a[x]]]==1) ans=max(ans,cnt[a[x]]);}
inline void Del(int x){if(vis[x]) if(--f[cnt[a[x]]--]==0) while(!f[ans]) ans--;}
int main()
{
int T=read();
while(T--){
memset(head,0,sizeof(head));
memset(son,0,sizeof(son));
memset(vis,0,sizeof(vis));
memset(f,0,sizeof(f));
memset(cnt,0,sizeof(cnt));
tim=tot=Cnum=Qnum=ans=now=0;
n=read(),m=read();blo=pow(2*n,0.666666);
int opt,x,y;
for(int i=1;i<n;i++){
x=read(),y=read();
add(x,y);add(y,x);
}
for(int i=1;i<=n;i++) a[i]=read();
dfs1(1,0);dfs2(1,1);
for(int i=1;i<=m;i++){
opt=read(),x=read(),y=read();
if(opt==0) pos[++Qnum]=x,val[Qnum]=y;
else {
q[++Cnum].anc=LCA(x,y);q[Cnum].pre=Qnum;q[Cnum].id=Cnum;
if(x==q[Cnum].anc||y==q[Cnum].anc){
q[Cnum].anc=0;
if(st[x]<st[y]) q[Cnum].l=st[x],q[Cnum].r=st[y];
else q[Cnum].l=st[y],q[Cnum].r=st[x];
}
else {
if(ed[x]<st[y]) q[Cnum].l=ed[x],q[Cnum].r=st[y];
else q[Cnum].l=ed[y],q[Cnum].r=st[x];
}
}
}
sort(q+1,q+Cnum+1,cmp);
int L=1,R=0;
for(int i=1;i<=Cnum;i++){
while(R<q[i].r) update(p[++R]);
while(R>q[i].r) update(p[R--]);
while(L<q[i].l) update(p[L++]);
while(L>q[i].l) update(p[--L]);
if(q[i].anc) update(q[i].anc);
while(now<q[i].pre){
now++;
Del(pos[now]);
swap(a[pos[now]],val[now]);
Add(pos[now]);
}
while(now>q[i].pre){
Del(pos[now]);
swap(a[pos[now]],val[now]);
Add(pos[now]);
now--;
}
Ans[q[i].id]=ans;
if(q[i].anc) update(q[i].anc);
}
for(int i=1;i<=Cnum;i++) printf("%d\n",Ans[i]);
}
return 0;
}
5、[WC2013]糖果公園
樹上帶修莫隊 我20分鐘切掉的
其實差不多一個板子,不過我被卡常了,我 \(inline+register+O_2\) 才過,最大一個點 \(5136ms\)
// luogu-judger-enable-o2
#include <bits/stdc++.h>
#define res register int
#define ll long long
using namespace std;
const int maxn=100000+10;
int n,m,Q,c[maxn],v[maxn],w[maxn],vis[maxn],cnt[maxn],pos[maxn],val[maxn],st[maxn],ed[maxn],p[maxn<<1],tim,blo,Cnum,Qnum,now;
int head[maxn],to[maxn<<1],nxt[maxn<<1],tot;ll Ans[maxn],ans;
int top[maxn],dep[maxn],siz[maxn],son[maxn],fa[maxn];
struct Query{
int l,r,anc,pre,id;
}q[maxn];
bool cmp(Query a,Query b){
if((a.l-1)/blo!=(b.l-1)/blo)
return (a.l-1)/blo<(b.l-1)/blo;
if(a.r<b.r) return a.r<b.r;
return a.pre<b.pre;
}
inline int read(){
register int x=0,f=1;char ch=getchar();
while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
return (f==1)?x:-x;
}
inline void add(res x,res y){
to[++tot]=y;
nxt[tot]=head[x];
head[x]=tot;
}
void dfs1(res x,res f){
siz[x]=1;fa[x]=f;
dep[x]=dep[f]+1;
st[x]=++tim;p[tim]=x;
int maxson=-1;
for(int i=head[x],y;i;i=nxt[i]){
y=to[i];
if(y==f) continue;
dfs1(y,x);
siz[x]+=siz[y];
if(maxson<siz[y]){
maxson=siz[y];
son[x]=y;
}
}
ed[x]=++tim;p[tim]=x;
}
void dfs2(res x,res topf){
top[x]=topf;
if(son[x]) dfs2(son[x],topf);
for(int i=head[x],y;i;i=nxt[i]){
y=to[i];
if(y==fa[x]||y==son[x]) continue;
dfs2(y,y);
}
}
inline int LCA(res x,res y){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]]) swap(x,y);
x=fa[top[x]];
}
if(dep[x]>dep[y]) swap(x,y);
return x;
}
inline void update(res x){
if(vis[x]){
ans-=(ll)v[c[x]]*w[cnt[c[x]]--];
}
else ans+=(ll)v[c[x]]*w[++cnt[c[x]]];
vis[x]^=1;
}
inline void Del(res x){if(vis[x]) ans-=(ll)v[c[x]]*w[cnt[c[x]]--];}
inline void Add(res x){if(vis[x]) ans+=(ll)v[c[x]]*w[++cnt[c[x]]];}
int main()
{
n=read(),m=read(),Q=read();blo=pow(2*n,0.666666);
for(res i=1;i<=m;i++) v[i]=read();
for(res i=1;i<=n;i++) w[i]=read();
res opt,x,y;
for(res i=1;i<n;i++){
x=read(),y=read();
add(x,y);add(y,x);
}
dfs1(1,0);dfs2(1,1);
for(res i=1;i<=n;i++) c[i]=read();
for(res i=1;i<=Q;i++){
opt=read(),x=read(),y=read();
if(opt==0) pos[++Qnum]=x,val[Qnum]=y;
else {
q[++Cnum].anc=LCA(x,y);q[Cnum].pre=Qnum;q[Cnum].id=Cnum;
if(x==q[Cnum].anc||y==q[Cnum].anc){
q[Cnum].anc=0;
if(st[x]<st[y]) q[Cnum].l=st[x],q[Cnum].r=st[y];
else q[Cnum].l=st[y],q[Cnum].r=st[x];
}
else {
if(ed[x]<st[y]) q[Cnum].l=ed[x],q[Cnum].r=st[y];
else q[Cnum].l=ed[y],q[Cnum].r=st[x];
}
}
}
sort(q+1,q+Cnum+1,cmp);
res L=1,R=0;
for(res i=1;i<=Cnum;i++){
while(R<q[i].r) update(p[++R]);
while(R>q[i].r) update(p[R--]);
while(L<q[i].l) update(p[L++]);
while(L>q[i].l) update(p[--L]);
if(q[i].anc) update(q[i].anc);
while(now<q[i].pre){
now++;
Del(pos[now]);
swap(c[pos[now]],val[now]);
Add(pos[now]);
}
while(now>q[i].pre){
Del(pos[now]);
swap(c[pos[now]],val[now]);
Add(pos[now]);
now--;
}
Ans[q[i].id]=ans;
if(q[i].anc) update(q[i].anc);
}
for(res i=1;i<=Cnum;i++) printf("%lld\n",Ans[i]);
return 0;
}