P3690 【模板】Link Cut Tree (动态树)
#include<cstdio>
#include<iostream>
using namespace std;
const int N=320000;
int w[N],n,m,opt,x,y;
struct LCT{
int ch[N][2],f[N],val[N],flag[N],tot,tmp[N];
int get(int x){
return ch[f[x]][1]==x;
}
void pushdown(int x){int l=ch[x][0],r=ch[x][1];
if(flag[x]){
flag[l]^=1;flag[r]^=1;flag[x]^=1;
swap(ch[x][0],ch[x][1]);
}
}
void update(int x){
val[x]=val[ch[x][0]]^val[ch[x][1]]^w[x];
}
bool isroot(int x){return ch[f[x]][0]!=x&&ch[f[x]][1]!=x;}
void retoto(int x){
int old=f[x],old2=f[old],opt=get(x),opt2=get(old);
if(!isroot(old)) ch[old2][opt2]=x;f[x]=old2;
f[old]=x;f[ch[x][opt^1]]=old;
ch[old][opt]=ch[x][opt^1];ch[x][opt^1]=old;
update(old);update(x);
}
void splay(int x){tot=1;tmp[1]=x;
for(int now=x;!isroot(now);now=f[now]) tmp[++tot]=f[now];
for(;tot;tot--) pushdown(tmp[tot]);
for(int fa;(fa=f[x])&&(!isroot(x));retoto(x))
if(!isroot(fa))retoto(get(x)==get(fa)?fa:x);
}//认父不认子
void access(int x){for(int y=0;x;y=x,x=f[x]) splay(x),ch[x][1]=y,update(x);}
void makeroot(int x){access(x);splay(x);flag[x]^=1;}
int oldroot(int x){access(x),splay(x);while(ch[x][0]) x=ch[x][0];return x;}
void split(int x,int y){makeroot(x);access(y);splay(y);}
void cut(int y,int x){split(x,y);if(x==ch[y][0]) ch[y][0]=0,f[x]=0;}
void link(int x,int y){makeroot(x);f[x]=y;}
int query(int x,int y){split(x,y);return val[y];}
}T;
int main(){scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&w[i]),T.val[i]=w[i];
while(m--){scanf("%d%d%d",&opt,&x,&y);
if(!opt) printf("%d\n",T.query(x,y));
else if(opt==1) {if(T.oldroot(x)!=T.oldroot(y)) T.link(x,y);}
else if(opt==2) {if(T.oldroot(x)==T.oldroot(y)) T.cut(x,y);}
else T.access(x),T.splay(x),w[x]=y,T.update(x);
}
}
P2147 [SDOI2008]Cave 洞穴勘测
#include<cstdio>
#include<iostream>
using namespace std;
const int N=20000;
int w[N],n,m,opt,x,y;char s[20];
struct LCT{
int ch[N][2],f[N],flag[N],tot,tmp[N];
int get(int x){
return ch[f[x]][1]==x;
}
void pushdown(int x){int l=ch[x][0],r=ch[x][1];
if(flag[x]){
flag[l]^=1;flag[r]^=1;flag[x]^=1;
swap(ch[x][0],ch[x][1]);
}
}
bool isroot(int x){return ch[f[x]][0]!=x&&ch[f[x]][1]!=x;}
void retoto(int x){
int old=f[x],old2=f[old],opt=get(x),opt2=get(old);
if(!isroot(old)) ch[old2][opt2]=x;f[x]=old2;
f[old]=x;f[ch[x][opt^1]]=old;
ch[old][opt]=ch[x][opt^1];ch[x][opt^1]=old;
}
void splay(int x){tot=1;tmp[1]=x;
for(int now=x;!isroot(now);now=f[now]) tmp[++tot]=f[now];
for(;tot;tot--) pushdown(tmp[tot]);
for(int fa;(fa=f[x])&&(!isroot(x));retoto(x))
if(!isroot(fa))retoto(get(x)==get(fa)?fa:x);
}//认父不认子
void access(int x){for(int y=0;x;y=x,x=f[x]) splay(x),ch[x][1]=y;}
void makeroot(int x){access(x);splay(x);flag[x]^=1;}
int oldroot(int x){access(x),splay(x);while(ch[x][0]) x=ch[x][0];return x;}
void split(int x,int y){makeroot(x);access(y);splay(y);}
void cut(int y,int x){split(x,y);if(x==ch[y][0]) ch[y][0]=0,f[x]=0;}
void link(int x,int y){makeroot(x);f[x]=y;}
}T;
int main(){scanf("%d%d",&n,&m);
while(m--){cin>>s;
scanf("%d%d",&x,&y);
if(s[0]=='Q') {if(T.oldroot(x)==T.oldroot(y))printf("Yes\n");else printf("No\n");}
else if(s[0]=='C') {if(T.oldroot(x)!=T.oldroot(y)) T.link(x,y);}
else if(s[0]=='D') {if(T.oldroot(x)==T.oldroot(y)) T.cut(x,y);}
}
}
P3703 [SDOI2017]树点涂色
这个题转化一下,一操作就是把他们弄到一棵树里,因为,多了一个颜色吗,这样虚子树的权值要+1,子树要减一
而每次都加修改1,差最大值,就是权值,所以树剖线段树维护
#include<cstdio>
#include<iostream>
using namespace std;
const int M=110000;
int n,m,to[2*M],nex[M*2],head[M],tot,f[M],dep[M],top[M],cnt,size[M],son[M],id[M],low[M];
void add(int x,int y){
nex[++tot]=head[x];
to[tot]=y;head[x]=tot;
}
struct xianduantree{
int maxn[4*M],ad[4*M];
void update(int x){
maxn[x]=max(maxn[x<<1],maxn[x<<1|1]);
}
void built(int o,int l,int r){
if(l==r) maxn[o]=0,ad[o]=0;
else {
int mid=(l+r)>>1;
built(o<<1,l,mid);
built(o<<1|1,mid+1,r);
update(o);
}
}
void pushdown(int o){
ad[o<<1]=(ad[o<<1]+ad[o]);
ad[o<<1|1]=(ad[o<<1|1]+ad[o]);
maxn[o<<1]=maxn[o<<1]+ad[o];
maxn[o<<1|1]=maxn[o<<1|1]+ad[o];
ad[o]=0;
}
void add(int o,int l,int r,int ql,int qr,int ins){
if(l>qr||r<ql) return ;
if(l>=ql&&r<=qr) {
ad[o]=(ad[o]+ins);
maxn[o]=maxn[o]+ins;
return ;
}
int mid=(l+r)>>1;
pushdown(o);
add(o<<1,l,mid,ql,qr,ins);
add(o<<1|1,mid+1,r,ql,qr,ins);
update(o);
}
int ask(int o,int l,int r,int ql,int qr){
if(l>qr||r<ql) return 0;
if(l>=ql&&r<=qr) return maxn[o];
int mid=(l+r)>>1;
pushdown(o);
return max(ask(o<<1,l,mid,ql,qr),ask(o<<1|1,mid+1,r,ql,qr));
}
}tr;
struct LCT{
int ch[M][2],f[M];
int get(int x){
return ch[f[x]][1]==x;
}
bool isroot(int x){return ch[f[x]][0]!=x&&ch[f[x]][1]!=x;}
void retoto(int x){
int old=f[x],old2=f[old],opt=get(x),opt2=get(old);
if(!isroot(old)) ch[old2][opt2]=x;f[x]=old2;
f[old]=x;f[ch[x][opt^1]]=old;
ch[old][opt]=ch[x][opt^1];ch[x][opt^1]=old;
}
void splay(int x){
for(int fa;(fa=f[x])&&(!isroot(x));retoto(x))
if(!isroot(fa))retoto(get(x)==get(fa)?fa:x);
}//认父不认子
void access(int x){for(int y=0;x;y=x,x=f[x]) {
splay(x);int t=ch[x][1];
while(ch[t][0]) t=ch[t][0];
tr.add(1,1,cnt,id[t],low[t],1);t=y;
while(ch[t][0]) t=ch[t][0];
tr.add(1,1,cnt,id[t],low[t],-1);
ch[x][1]=y;
}}
}T;
int dfs1(int x){
size[x]=1;dep[x]=dep[f[x]]+1;
int maxsi=0;
for(int i=head[x];i;i=nex[i]){
int tmp=to[i];
if(tmp!=f[x]){
f[tmp]=x;
int siz=dfs1(tmp);
size[x]+=siz;
if(maxsi<siz)
son[x]=tmp,maxsi=siz;
}
}
return size[x];
}
void dfs2(int x,int topx){
top[x]=topx;
id[x]=++cnt;
if(!son[x]){low[x]=cnt;return ;}
dfs2(son[x],topx);
for(int i=head[x],tmp;i;i=nex[i])
if(!id[tmp=to[i]])
dfs2(tmp,tmp);
low[x]=cnt;
}
int lca(int x,int y){
while(top[x]!=top[y]){
if(dep[top[x]]<dep[top[y]])swap(x,y);
x=f[top[x]];
}
return dep[x]>dep[y]?y:x;
}
int main(){scanf("%d%d",&n,&m);
for(int i=1,x,y;i<n;i++) scanf("%d%d",&x,&y),add(x,y),add(y,x);
dfs1(1);dfs2(1,1);
tr.built(1,1,cnt);
for(int i=1;i<=n;i++) T.f[i]=f[i],tr.add(1,1,cnt,id[i],low[i],1);
while(m--){int x,y,opt;
scanf("%d%d",&opt,&x);
if(opt==1) T.access(x);
else if(opt==2) scanf("%d",&y),opt=lca(x,y),printf("%d\n",tr.ask(1,1,cnt,id[x],id[x])
+tr.ask(1,1,cnt,id[y],id[y])-2*tr.ask(1,1,cnt,id[opt],id[opt])+1);
else printf("%d\n",tr.ask(1,1,cnt,id[x],low[x]));
}
}
P3203 [HNOI2010]弹飞绵羊
#include<cstdio>
#include<iostream>
using namespace std;
const int N=420000;
int w[N],n,m,opt,x,y;
struct LCT{
int ch[N][2],f[N],val[N],flag[N],tot,tmp[N];
int get(int x){
return ch[f[x]][1]==x;
}
void pushdown(int x){int l=ch[x][0],r=ch[x][1];
if(flag[x]){
flag[l]^=1;flag[r]^=1;flag[x]^=1;
swap(ch[x][0],ch[x][1]);
}
}
void update(int x){
val[x]=val[ch[x][0]]+val[ch[x][1]]+1;
}
bool isroot(int x){return ch[f[x]][0]!=x&&ch[f[x]][1]!=x;}
void retoto(int x){
int old=f[x],old2=f[old],opt=get(x),opt2=get(old);
if(!isroot(old)) ch[old2][opt2]=x;f[x]=old2;
f[old]=x;f[ch[x][opt^1]]=old;
ch[old][opt]=ch[x][opt^1];ch[x][opt^1]=old;
update(old);update(x);
}
void splay(int x){tot=1;tmp[1]=x;
for(int now=x;!isroot(now);now=f[now]) tmp[++tot]=f[now];
for(;tot;tot--) pushdown(tmp[tot]);
for(int fa;(fa=f[x])&&(!isroot(x));retoto(x))
if(!isroot(fa))retoto(get(x)==get(fa)?fa:x);
}//认父不认子
void access(int x){for(int y=0;x;y=x,x=f[x]) splay(x),ch[x][1]=y,update(x);}
void makeroot(int x){access(x);splay(x);flag[x]^=1;}
int oldroot(int x){access(x),splay(x);while(ch[x][0]) x=ch[x][0];return x;}
void split(int x,int y){makeroot(x);access(y);splay(y);}
void cut(int y,int x){split(x,y);if(x==ch[y][0]) ch[y][0]=0,f[x]=0;}
void link(int x,int y){makeroot(x);f[x]=y;}
int query(int x){makeroot(n+1);access(x);splay(x);return val[ch[x][0]];}
}T;
int main(){scanf("%d",&n);
for(int i=1;i<=n+1;i++) T.val[i]=1;
for(int i=1;i<=n;i++) {
scanf("%d",&w[i]);
T.f[i]=(i+w[i]<=n)?i+w[i]:(n+1);
}
scanf("%d",&m);
while(m--){scanf("%d%d",&opt,&x);x++;
if(opt==1) printf("%d\n",T.query(x));
else if(opt==2) {
scanf("%d",&y);T.cut(x,(w[x]+x<=n)?x+w[x]:(n+1));T.link(x,(y+x<=n)?x+y:(n+1));w[x]=y;
}
}
}
P2387 [NOI2014]魔法森林
这个题是一个二维问题,没有修改,直接用扫描线,降维
然后LCA维护最小生成树和最大边;
// luogu-judger-enable-o2
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
const int N=320000,INF=510000;
int w[N],n,m,opt,x,y,s[N],t[N],id,ans=INF;
struct node{
int u,v,a,b;
}e[N];
struct LCT{
int ch[N][2],f[N],val[N],flag[N],tot,tmp[N],maxs[N];
int get(int x){
return ch[f[x]][1]==x;
}
void pushdown(int x){int l=ch[x][0],r=ch[x][1];
if(flag[x]){
flag[l]^=1;flag[r]^=1;flag[x]^=1;
swap(ch[x][0],ch[x][1]);
}
}
void update(int x){
maxs[x]=x;
if(val[maxs[ch[x][0]]]>val[maxs[x]]) maxs[x]=maxs[ch[x][0]];
if(val[maxs[ch[x][1]]]>val[maxs[x]]) maxs[x]=maxs[ch[x][1]];
}
bool isroot(int x){return ch[f[x]][0]!=x&&ch[f[x]][1]!=x;}
void retoto(int x){
int old=f[x],old2=f[old],opt=get(x),opt2=get(old);
if(!isroot(old)) ch[old2][opt2]=x;f[x]=old2;
f[old]=x;f[ch[x][opt^1]]=old;
ch[old][opt]=ch[x][opt^1];ch[x][opt^1]=old;
update(old);update(x);
}
void splay(int x){tot=1;tmp[1]=x;
for(int now=x;!isroot(now);now=f[now]) tmp[++tot]=f[now];
for(;tot;tot--) pushdown(tmp[tot]);
for(int fa;(fa=f[x])&&(!isroot(x));retoto(x))
if(!isroot(fa))retoto(get(x)==get(fa)?fa:x);
}//认父不认子
void access(int x){for(int y=0;x;y=x,x=f[x]) splay(x),ch[x][1]=y,update(x);}
void makeroot(int x){access(x);splay(x);flag[x]^=1;}
int oldroot(int x){access(x),splay(x);while(ch[x][0]) x=ch[x][0];return x;}
void split(int x,int y){makeroot(x);access(y);splay(y);}
void cut(int y,int x){split(x,y);if(x==ch[y][0]) ch[y][0]=0,f[x]=0;}
void link(int x,int y){makeroot(x);f[x]=y;}
void add(int x,int y,int z){
if(oldroot(x)^oldroot(y)){id++;
s[id]=x,t[id]=y;
val[id]=z,maxs[id]=id;
link(x,id),link(y,id);
}
else{
split(x,y);
int v=maxs[y];
if(val[v]<z) return;
cut(s[v],v),cut(t[v],v);id++;
s[id]=x,t[id]=y;
val[id]=z,maxs[id]=id;
link(x,id),link(y,id);
}
}
int query(int x,int y){if(oldroot(x)^oldroot(y)) return INF;split(x,y);
return val[maxs[y]];}
}T;
bool cmp(node x,node y){
return x.a!=y.a?x.a<y.a:x.b<y.b;
}
int main(){scanf("%d%d",&n,&m);id=n;
for(int i=1;i<=m;i++)
scanf("%d%d%d%d",&e[i].u,&e[i].v,&e[i].a,&e[i].b);
sort(e+1,e+m+1,cmp);
for(int i=1;i<=m;i++){
T.add(e[i].u,e[i].v,e[i].b);
ans=min(ans,e[i].a+T.query(1,n));
}
printf("%d\n",ans==INF?-1:ans);
}
P4172 [WC2006]水管局长
这个题离线做,把删边弄成加边
LCT维护最小生成树和最大边
#include<algorithm>
#include<iostream>
#define N 1500000
#include<cstdio>
using namespace std;
struct LCT{
int ch[N][2],f[N],val[N],flag[N],tot,tmp[N],maxs[N];
int get(int x){
return ch[f[x]][1]==x;
}
void pushdown(int x){int l=ch[x][0],r=ch[x][1];
if(flag[x]){
flag[l]^=1;flag[r]^=1;flag[x]^=1;
swap(ch[x][0],ch[x][1]);
}
}
void update(int x){
maxs[x]=x;
if(val[maxs[ch[x][0]]]>val[maxs[x]]) maxs[x]=maxs[ch[x][0]];
if(val[maxs[ch[x][1]]]>val[maxs[x]]) maxs[x]=maxs[ch[x][1]];
}
bool isroot(int x){return ch[f[x]][0]!=x&&ch[f[x]][1]!=x;}
void retoto(int x){
int old=f[x],old2=f[old],opt=get(x),opt2=get(old);
if(!isroot(old)) ch[old2][opt2]=x;f[x]=old2;
f[old]=x;f[ch[x][opt^1]]=old;
ch[old][opt]=ch[x][opt^1];ch[x][opt^1]=old;
update(old);update(x);
}
void splay(int x){tot=1;tmp[1]=x;
for(int now=x;!isroot(now);now=f[now]) tmp[++tot]=f[now];
for(;tot;tot--) pushdown(tmp[tot]);
for(int fa;(fa=f[x])&&(!isroot(x));retoto(x))
if(!isroot(fa))retoto(get(x)==get(fa)?fa:x);
}//认父不认子
void access(int x){for(int y=0;x;y=x,x=f[x]) splay(x),ch[x][1]=y,update(x);}
void makeroot(int x){access(x);splay(x);flag[x]^=1;}
int oldroot(int x){access(x),splay(x);while(ch[x][0]) x=ch[x][0];return x;}
void split(int x,int y){makeroot(x);access(y);splay(y);}
void cut(int y,int x){split(x,y);if(x==ch[y][0]) ch[y][0]=0,f[x]=0;}
void link(int x,int y){makeroot(x);f[x]=y;}
int query(int x,int y){split(x,y);return maxs[y];}
}T;
struct eage{
int u,v,l,id;
bool f;
}e[N],q[N];
int n,m,Q;int f[N],ans[N];
bool comp1(eage aa,eage bb){
return aa.u==bb.u?aa.v<bb.v:aa.u<bb.u;
}
bool comp2(eage aa,eage bb){
return aa.l<bb.l;
}
bool comp3(eage aa,eage bb){
return aa.id<bb.id;
}
int findit(int u,int v){
int l=1,r=m;
while(l<=r){
int mid=(l+r)>>1;
if(e[mid].u<u||(e[mid].u==u&&e[mid].v<v))l=mid+1;
else if(e[mid].u==u&&e[mid].v==v)return mid;
else r=mid-1;
}
}
int get_f(int x){
return x==f[x]?f[x]:f[x]=get_f(f[x]);
}
void KUSCER(){
for(int i=1;i<=n;++i) f[i]=i;
sort(e+1,e+m+1,comp3);int tot=0;
for(int i=1;i<=m;++i){
if(e[i].f) continue;
int x=get_f(e[i].u),y=get_f(e[i].v);
if(x==y) continue;tot++;
f[x]=y;
T.link(e[i].u,i+n);
T.link(e[i].v,i+n);
if(tot==n-1) return;
}
}
int main()
{
scanf("%d%d%d",&n,&m,&Q);
for(int i=1;i<=m;++i){
scanf("%d%d%d",&e[i].u,&e[i].v,&e[i].l);
if(e[i].u>e[i].v) swap(e[i].u,e[i].v);
}
sort(e+1,e+m+1,comp2);
for(int i=1;i<=m;++i){
e[i].id=i;
T.val[n+i]=e[i].l;
T.maxs[n+i]=n+i;
}
sort(e+1,e+m+1,comp1);
for(int i=1,www;i<=Q;++i){
scanf("%d%d%d",&www,&q[i].u,&q[i].v);www--;q[i].f=www;
if(q[i].f==1){
if(q[i].u>q[i].v) swap(q[i].u,q[i].v);
int id=findit(q[i].u,q[i].v);
q[i].id=e[id].id;e[id].f=1;
}
}
KUSCER();
for(int i=Q;i;--i) {
if(q[i].f==0) ans[i]=T.val[T.query(q[i].u,q[i].v)];
else{
int bi=T.query(q[i].u,q[i].v);
if(T.val[bi]>T.val[q[i].id+n]){
T.cut(e[bi-n].u,bi);T.cut(e[bi-n].v,bi);
T.link(q[i].u,q[i].id+n);T.link(q[i].v,q[i].id+n);
}
}
}
for(int i=1;i<=Q;++i) if(q[i].f==0) printf("%d\n",ans[i]);
return 0;
}
P4219 [BJOI2014]大融合
我们发现这个题其实就是求虚子树个数的乘积
直接lct维护子树信息
#include<algorithm>
#include<iostream>
#define N 1500000
#include<cstdio>
using namespace std;
struct LCT{
int ch[N][2],f[N],size[N],flag[N],tot,tmp[N],sise[N];
int get(int x){
return ch[f[x]][1]==x;
}
void pushdown(int x){int l=ch[x][0],r=ch[x][1];
if(flag[x]){
flag[l]^=1;flag[r]^=1;flag[x]^=1;
swap(ch[x][0],ch[x][1]);
}
}
void update(int x){
size[x]=size[ch[x][0]]+size[ch[x][1]]+1+sise[x];
}
bool isroot(int x){return ch[f[x]][0]!=x&&ch[f[x]][1]!=x;}
void retoto(int x){
int old=f[x],old2=f[old],opt=get(x),opt2=get(old);
if(!isroot(old)) ch[old2][opt2]=x;f[x]=old2;
f[old]=x;f[ch[x][opt^1]]=old;
ch[old][opt]=ch[x][opt^1];ch[x][opt^1]=old;
update(old);update(x);
}
void splay(int x){tot=1;tmp[1]=x;
for(int now=x;!isroot(now);now=f[now]) tmp[++tot]=f[now];
for(;tot;tot--) pushdown(tmp[tot]);
for(int fa;(fa=f[x])&&(!isroot(x));retoto(x))
if(!isroot(fa))retoto(get(x)==get(fa)?fa:x);
}//认父不认子
void access(int x){for(int y=0;x;y=x,x=f[x]) splay(x),sise[x]+=size[ch[x][1]],sise[x]-=size[ch[x][1]=y],update(x);}
void makeroot(int x){access(x);splay(x);flag[x]^=1;}
int oldroot(int x){access(x),splay(x);while(ch[x][0]) x=ch[x][0];return x;}
void split(int x,int y){makeroot(x);access(y);splay(y);}
void cut(int y,int x){split(x,y);if(x==ch[y][0]) ch[y][0]=0,f[x]=0;}
void link(int x,int y){split(x,y);f[x]=y;sise[y]+=size[x];update(y);}
}T;
int n,Q;
int main()
{
scanf("%d%d%d",&n,&Q);
for(int i=1;i<=n;i++) T.size[i]=1;
while(Q--){char s[10];int x,y;
scanf("%s%d%d",s,&x,&y);
if(s[0]=='A') T.link(x,y);
else {T.split(x,y);
printf("%lld\n",1ll*(T.sise[x]+1)*(T.sise[y]+1));
}
}
return 0;
}