link cut tree 入门

时间:2022-01-03 20:52:21

鉴于最近写bzoj还有51nod都出现写不动的现象,决定学习一波厉害的算法/数据结构。

link cut tree:研究popoqqq那个神ppt。

bzoj1036:维护access操作就可以了。

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#include<queue>
using namespace std;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define clr(x,c) memset(x,c,sizeof(x))
#define qwq(x) for(edge *o=head[x];o;o=o->next)
int read(){
int x=0,f=1;char c=getchar();
while(!isdigit(c)){
if(c=='-') f=-1;c=getchar();
}
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x*f;
}
const int nmax=3e4+5;
const int inf=0x7f7f7f7f;
struct edge{
int to;edge *next;
};
edge es[nmax<<1],*pt=es,*head[nmax];
void add(int u,int v){
pt->to=v;pt->next=head[u];head[u]=pt++;
pt->to=u;pt->next=head[v];head[v]=pt++;
}
int n,m,fa[nmax],c[nmax][2],sm[nmax],mx[nmax],q[nmax],w[nmax];bool vis[nmax];
void bfs(){
int l=1,r=1,x;q[r]=1;vis[1]=1;
while(l<=r){
x=q[l++];
qwq(x) if(!vis[o->to]) fa[o->to]=x,q[++r]=o->to,vis[o->to]=1;
}
}
bool pdrt(int x){
return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;
}
void update(int x){
int l=c[x][0],r=c[x][1];
sm[x]=sm[l]+sm[r]+w[x];mx[x]=max(w[x],max(mx[l],mx[r]));
}
void rotate(int x){
int y=fa[x],z=fa[y],l=c[y][1]==x,r=l^1;
if(!pdrt(y)) if(c[z][0]==y) c[z][0]=x;else c[z][1]=x;
fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
c[y][l]=c[x][r];c[x][r]=y;
update(y);update(x);
}
void splay(int x){ //把一个点伸展到它所在的重链的根。
int y,z;//fa[x]=y表示以x为根的splay树的父亲是y,但是y的两个儿子(c[y][0]和c[y][1])中没有一个是x
while(!pdrt(x)){
y=fa[x];z=fa[y];
if(!pdrt(y)) if(c[y][0]==x^c[z][0]==y) rotate(x);else rotate(y);
rotate(x);
}
}
int access(int x){
int t=0;
while(x) splay(x),c[x][1]=t,t=x,update(x),x=fa[x];
return t;
//考虑点u,v。假设先access(u)了,那么access(lca(u,v))时lca(u,v)是原splay的根节点,
//左边的是深度比它小的点,即是lca(u,v)到根的链。 说明access的操作是正确的。
}
void qsum(int u,int v){
access(u);int lca=access(v);splay(u);
if(lca==u) printf("%d\n",w[u]+sm[c[lca][1]]);
else printf("%d\n",w[lca]+sm[c[lca][1]]+sm[u]);
}
void qmax(int u,int v){
access(u);int lca=access(v);splay(u);
if(lca==u) printf("%d\n",max(w[u],mx[c[lca][1]]));
else printf("%d\n",max(w[lca],max(mx[c[lca][1]],mx[u])));
}
int main(){
n=read();int u,v,d;mx[0]=-inf;
rep(i,1,n-1) u=read(),v=read(),add(u,v);
rep(i,1,n) sm[i]=mx[i]=w[i]=read();bfs();
m=read();char ch[11];
rep(i,1,m){
scanf("%s",ch);u=read(),v=read();
if(ch[1]=='H') splay(u),w[u]=v,update(u);
else if(ch[1]=='S') qsum(u,v);
else qmax(u,v);
}
return 0;
}

bzoj2049:link cut tree 模版题。

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
using namespace std;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define clr(x,c) memset(x,c,sizeof(x))
#define qwq(x) for(edge *o=head[x];o;o=o->next)
int read(){
int x=0,f=1;char c=getchar();
while(!isdigit(c)){
if(c=='-') f=-1;c=getchar();
}
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x*f;
}
const int nmax=1e4+5;
int n,m,fa[nmax],c[nmax][2],st[nmax],rev[nmax];
bool pdrt(int x){
return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;
}
void pushdown(int x){
if(rev[x]){
int l=c[x][0],r=c[x][1];
rev[x]^=1;rev[l]^=1;rev[r]^=1;
swap(c[x][0],c[x][1]);
}
}
void rotate(int x){
int y=fa[x],z=fa[y],l=c[y][1]==x,r=l^1;
if(!pdrt(y)) if(c[z][0]==y) c[z][0]=x;else c[z][1]=x;
fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
c[y][l]=c[x][r];c[x][r]=y;
}
void splay(int x){
int top=0,y,z;st[++top]=x;
for(int i=x;!pdrt(i);i=fa[i]) st[++top]=fa[i];
dwn(i,top,1) pushdown(st[i]);
while(!pdrt(x)){
y=fa[x];z=fa[y];
if(!pdrt(y)) if(c[y][0]==x^c[z][0]==y) rotate(x);else rotate(y);
rotate(x);
}
}
void access(int x){
int t=0;
while(x) splay(x),c[x][1]=t,t=x,x=fa[x];
}
void rever(int x){
access(x);splay(x);rev[x]^=1;
}
void link(int x,int y){
rever(x);fa[x]=y;
}
void cut(int x,int y){
rever(x);access(y);splay(y);c[y][0]=fa[x]=0;
}
int find(int x){
access(x);splay(x);
int y=x;while(c[y][0]) y=c[y][0];
return y;
}
int main(){
n=read(),m=read();int x,y;char ch[11];
rep(i,1,m){
scanf("%s",ch);x=read(),y=read();
if(ch[0]=='C') link(x,y);
else if(ch[0]=='D') cut(x,y);
else {
if(find(x)==find(y)) puts("Yes");
else puts("No");
}
}
return 0;
}

我这二逼智商。。。真的够了。。。。

upd9.27 重新理解了两次link cut tree 然后打起代码来顺多了。。。

bzoj2002:还是基本的操作

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
using namespace std;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define clr(x,c) memset(x,c,sizeof(x))
int read(){
int x=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x;
}
const int nmax=2e5+5;
const int inf=0x7f7f7f7f;
int fa[nmax],c[nmax][2],sz[nmax],q[nmax],nxt[nmax];
bool rev[nmax];
bool isroot(int x){
return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;
}
void pushup(int x){
sz[x]=sz[c[x][0]]+sz[c[x][1]]+1;
}
void pushdown(int x){
if(rev[x]){
int l=c[x][0],r=c[x][1];
rev[x]=0;rev[l]^=1;rev[r]^=1;
swap(c[x][0],c[x][1]);
}
}
void rotate(int x){
int y=fa[x],z=fa[y],l=c[y][1]==x,r=l^1;
if(!isroot(y)) c[z][c[z][1]==y]=x;
fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
c[y][l]=c[x][r];c[x][r]=y;
pushup(y);pushup(x);
}
void splay(int x){
int cnt=0,y,z;q[++cnt]=x;
for(int i=x;!isroot(i);i=fa[i]) q[++cnt]=fa[i];
dwn(i,cnt,1) pushdown(q[i]);
while(!isroot(x)){
y=fa[x],z=fa[y];
if(!isroot(y)){
if(c[y][0]==x^c[z][0]==y) rotate(x);
else rotate(y);
}
rotate(x);
}
}
void access(int x){
int t=0;
while(x) splay(x),c[x][1]=t,t=x,x=fa[x]; //这里应该是要pushup(x)的。但是为什么不加也能A啊TAT
}
void rever(int x){
access(x),splay(x),rev[x]^=1;
}
void link(int x,int y){
rever(x);fa[x]=y;
}
void cut(int x,int y){
rever(x);access(y);splay(y);c[y][0]=fa[x]=0;
}
int main(){
int n=read(),u,v,d;
rep(i,1,n){
u=read();fa[i]=min(n+1,i+u);
sz[i]=1;nxt[i]=fa[i];
}
sz[n+1]=1;
int m=read();
while(m--){
u=read();
if(u==1){
rever(n+1);v=read()+1;
access(v);splay(v);
printf("%d\n",sz[c[v][0]]);
}else{
v=read()+1;d=read();u=min(n+1,v+d);
cut(v,nxt[v]);link(v,u);nxt[v]=u;
}
}
return 0;
}
/*
4
1 2 1 1
3
1 1
2 1 1
1 1
*/

bzoj3282:还是基本操作

因为pushup我放在了x=fa[x]后面调了一节晚自修。

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
using namespace std;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define clr(x,c) memset(x,c,sizeof(x))
int read(){
int x=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x;
}
const int nmax=3e5+5;
const int inf=0x7f7f7f7f;
int fa[nmax],c[nmax][2],sz[nmax],w[nmax],q[nmax];
bool rev[nmax];
void pushup(int x){
sz[x]=w[x]^sz[c[x][0]]^sz[c[x][1]];
}
void pushdown(int x){
if(rev[x]){
int l=c[x][0],r=c[x][1];
rev[x]=0;rev[l]^=1;rev[r]^=1;
swap(c[x][0],c[x][1]);
}
}
bool isroot(int x){
return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;
}
void rotate(int x){
int y=fa[x],z=fa[y],l=c[y][1]==x,r=l^1;
if(!isroot(y)) c[z][c[z][1]==y]=x;
fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
c[y][l]=c[x][r];c[x][r]=y;
pushup(y);pushup(x);
}
void splay(int x){
int cnt=0,y,z;q[++cnt]=x;
for(int i=x;!isroot(i);i=fa[i]) q[++cnt]=fa[i];
dwn(i,cnt,1) pushdown(q[i]);
while(!isroot(x)){
y=fa[x];z=fa[y];
if(!isroot(y)){
if(c[y][0]==x^c[z][0]==y) rotate(x);
else rotate(y);
}
rotate(x);
}
}
void access(int x){
int t=0;
while(x) splay(x),c[x][1]=t,pushup(x),t=x,x=fa[x];
}
//因为pushup我放在了x=fa[x]后面调了一节晚自修。
void rever(int x){
access(x),splay(x),rev[x]^=1;
}
void link(int x,int y){
rever(x);fa[x]=y;
}
void cut(int x,int y){
rever(x);access(y);splay(y);c[y][0]=fa[x]=0;
}
int find(int x){
access(x);splay(x);
while(c[x][0]) x=c[x][0];
return x;
}
int main(){
int n=read(),m=read(),u,x,y;
rep(i,1,n) sz[i]=w[i]=read();
while(m--){
u=read(),x=read(),y=read();
if(u==0) {
rever(x);access(y),splay(y);
printf("%d\n",sz[y]);
}else if(u==1) {
if(find(x)!=find(y)) link(x,y);
}else if(u==2) {
if(find(x)==find(y)) cut(x,y);
}else {
access(x),splay(x),w[x]=y,pushup(x);
}
}
return 0;
}

bzoj2843&1180:还是基本操作

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
using namespace std;
#define rep(i,s,t) for(register int i=s;i<=t;i++)
#define dwn(i,s,t) for(register int i=s;i>=t;i--)
#define clr(x,c) memset(x,c,sizeof(x))
#define lc c[x][0]
#define rc c[x][1]
int read(){
int x=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x;
}
const int nmax=3e4+5;
const int inf=0x7f7f7f7f;
int fa[nmax],c[nmax][2],sm[nmax],w[nmax],q[nmax];
bool rev[nmax];
bool isroot(int x){
int t=x;x=fa[x];return lc!=t&&rc!=t;
}
void pushup(int x){
sm[x]=w[x]+sm[lc]+sm[rc];
}
void pushdown(int x){
if(rev[x]){
rev[x]=0;rev[lc]^=1;rev[rc]^=1;
swap(lc,rc);
}
}
void rotate(int x){
int y=fa[x],z=fa[y],l=c[y][1]==x,r=l^1;
if(!isroot(y)) c[z][c[z][1]==y]=x;
fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
c[y][l]=c[x][r];c[x][r]=y;
pushup(y);pushup(x);
}
void splay(int x){
int cnt=0,y,z;q[++cnt]=x;
for(register int i=x;!isroot(i);i=fa[i]) q[++cnt]=fa[i];
dwn(i,cnt,1) pushdown(q[i]);
while(!isroot(x)){
y=fa[x];z=fa[y];
if(!isroot(y)) {
if(c[y][0]==x^c[z][0]==y) rotate(x);
else rotate(y);
}
rotate(x);
}
}
void access(int x){
int t=0;
while(x) splay(x),c[x][1]=t,pushup(x),t=x,x=fa[x];
}
void rever(int x){
access(x);splay(x);rev[x]^=1;
}
void link(int x,int y){
rever(x);fa[x]=y;if(!(x&233)) splay(x);//都不splayx或者都splayx会比较慢。。。
}
void cut(int x,int y){
rever(x);access(y);splay(y);c[y][0]=fa[x]=0;
}
int find(int x){
access(x);splay(x);
while(c[x][0]) x=c[x][0];
return x;
}
int main(){
int n=read();
rep(i,1,n) w[i]=sm[i]=read();
int m=read(),u,v,d;char s[15];
while(m--){
scanf("%s",s);u=read(),v=read();
if(s[0]=='b') {
if(find(u)!=find(v)) link(u,v),puts("yes");
else puts("no");
}else if(s[0]=='e'){
if(find(u)!=find(v)) puts("impossible");
else rever(u),access(v),splay(v),printf("%d\n",sm[v]);
}else{
splay(u);w[u]=v;pushup(u);
}
}
return 0;
}

bzoj3669:

3669: [Noi2014]魔法森林

Time Limit: 30 Sec  Memory Limit: 512 MB
Submit: 2156  Solved: 1310
[Submit][Status][Discuss]

Description

为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士。魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M。初始时小E同学在号节点1,隐士则住在号节点N。小E需要通过这一片魔法森林,才能够拜访到隐士。

魔法森林中居住了一些妖怪。每当有人经过一条边的时候,这条边上的妖怪就会对其发起攻击。幸运的是,在号节点住着两种守护精灵:A型守护精灵与B型守护精灵。小E可以借助它们的力量,达到自己的目的。

只要小E带上足够多的守护精灵,妖怪们就不会发起攻击了。具体来说,无向图中的每一条边Ei包含两个权值Ai与Bi。若身上携带的A型守护精灵个数不少于Ai,且B型守护精灵个数不少于Bi,这条边上的妖怪就不会对通过这条边的人发起攻击。当且仅当通过这片魔法森林的过程中没有任意一条边的妖怪向小E发起攻击,他才能成功找到隐士。

由于携带守护精灵是一件非常麻烦的事,小E想要知道,要能够成功拜访到隐士,最少需要携带守护精灵的总个数。守护精灵的总个数为A型守护精灵的个数与B型守护精灵的个数之和。

Input

第1行包含两个整数N,M,表示无向图共有N个节点,M条边。 接下来M行,第行包含4个正整数Xi,Yi,Ai,Bi,描述第i条无向边。其中Xi与Yi为该边两个端点的标号,Ai与Bi的含义如题所述。 注意数据中可能包含重边与自环。

Output

输出一行一个整数:如果小E可以成功拜访到隐士,输出小E最少需要携带的守护精灵的总个数;如果无论如何小E都无法拜访到隐士,输出“-1”(不含引号)。

WerKeyTom_FTD 神犇的题解:

我们尝试枚举路径的最大a值,那么我们只需按照a排序按顺序插入,维护1到n的b最大值即可。
用并查集维护连通性。当加入j到k这条边时如果形成环,则删除环上的最大值。
我们用动态树来进行维护。
为了实现更易,将每条边看做一个点,例如第i条边两个端点是j与k,那么将i+n与j、k相连,权值放在代表边的点上。

然后莫名其妙的空间开小了居然是TLE重新打一遍的过程中突然想到nmax开小了TAT

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
using namespace std;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define clr(x,c) memset(x,c,sizeof(x))
int read(){
int x=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x;
}
const int nmax=2e5+5;
const int maxn=1e5+5;
const int inf=0x7f7f7f7f;
int fa[nmax],c[nmax][2],mx[nmax],q[nmax],p[nmax],w[nmax];
bool rev[nmax];
struct edge{
int u,v,a,b;
bool operator<(const edge &rhs)const{
return a<rhs.a;}
};
edge es[maxn];
bool isroot(int x){
return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;
}
void pushup(int x){
int l=c[x][0],r=c[x][1];
mx[x]=x;
if(w[mx[x]]<w[mx[l]]) mx[x]=mx[l];
if(w[mx[x]]<w[mx[r]]) mx[x]=mx[r];
}
void pushdown(int x){
if(rev[x]){
rev[x]=0;rev[c[x][0]]^=1;rev[c[x][1]]^=1;
swap(c[x][0],c[x][1]);
}
}
void rotate(int x){
int y=fa[x],z=fa[y],l=c[y][1]==x,r=l^1;
if(!isroot(y)) c[z][c[z][1]==y]=x;
fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
c[y][l]=c[x][r];c[x][r]=y;
pushup(y);pushup(x);
}
void splay(int x){
int cnt=0,y,z;q[++cnt]=x;
for(register int i=x;!isroot(i);i=fa[i]) q[++cnt]=fa[i];
dwn(i,cnt,1) pushdown(q[i]);
while(!isroot(x)){
y=fa[x];z=fa[y];
if(!isroot(y)){
if(c[y][0]==x^c[z][0]==y) rotate(x);
else rotate(y);
}
rotate(x);
}
}
void access(int x){
int t=0;
while(x) splay(x),c[x][1]=t,pushup(x),t=x,x=fa[x];
}
void rever(int x){
access(x);splay(x);rev[x]^=1;
}
void link(int x,int y){
rever(x);fa[x]=y;if(x&233) splay(x);
}
void cut(int x,int y){
rever(x);access(y);splay(y);c[y][0]=fa[x]=0;pushup(y);
}
int find(int x){
return p[x]==x?x:p[x]=find(p[x]);
}
int query(int x,int y){
rever(x);access(y),splay(y);return mx[y];
}
void mins(int &a,int b){
if(a>b) a=b;
}
int main(){
int n=read(),m=read(),u,v,d,tmp,temp,ta,tb;
rep(i,1,m) es[i].u=read(),es[i].v=read(),es[i].a=read(),es[i].b=read();
sort(es+1,es+m+1);
int ans=inf;rep(i,1,n) p[i]=i;
rep(i,1,m){
u=es[i].u,v=es[i].v;ta=find(u),tb=find(v);
if(ta==tb){
d=query(u,v);
if(w[d]>es[i].b) cut(d,es[d-n].u),cut(d,es[d-n].v);
else {
if(find(1)==find(n)) mins(ans,es[i].a+w[query(1,n)]);
continue;
}
}else p[ta]=tb;
w[n+i]=es[i].b;mx[n+i]=n+i;
link(u,n+i),link(v,n+i);
if(find(1)==find(n)) mins(ans,es[i].a+w[query(1,n)]);
}
printf("%d\n",ans==inf?-1:ans);
return 0;
}
/*
4 5
1 2 19 1
2 3 8 12
2 4 12 15
1 3 17 8
3 4 1 17
*/

bzoj2594:

2594: [Wc2006]水管局长数据加强版

Time Limit: 25 Sec  Memory Limit: 128 MB
Submit: 2600  Solved: 840
[Submit][Status][Discuss]

Description

SC省MY市有着庞大的地下水管网络,嘟嘟是MY市的水管局长(就是管水管的啦),嘟嘟作为水管局长的工作就是:每天供水公司可能要将一定量的水从x处送往y处,嘟嘟需要为供水公司找到一条从A至B的水管的路径,接着通过信息化的控制中心通知路径上的水管进入准备送水状态,等到路径上每一条水管都准备好了,供水公司就可以开始送水了。嘟嘟一次只能处理一项送水任务,等到当前的送水任务完成了,才能处理下一项。
在处理每项送水任务之前,路径上的水管都要进行一系列的准备操作,如清洗、消毒等等。嘟嘟在控制中心一声令下,这些水管的准备操作同时开始,但由于各条管道的长度、内径不同,进行准备操作需要的时间可能不同。供水公司总是希望嘟嘟能找到这样一条送水路径,路径上的所有管道全都准备就绪所需要的时间尽量短。嘟嘟希望你能帮助他完成这样的一个选择路径的系统,以满足供水公司的要求。另外,由于MY市的水管年代久远,一些水管会不时出现故障导致不能使用,你的程序必须考虑到这一点。
不妨将MY市的水管网络看作一幅简单无向图(即没有自环或重边):水管是图中的边,水管的连接处为图中的结点。
 

Input

输入文件第一行为3个整数:N, M, Q分别表示管道连接处(结点)的数目、目前水管(无向边)的数目,以及你的程序需要处理的任务数目(包括寻找一条满足要求的路径和接受某条水管坏掉的事实)。
以下M行,每行3个整数x, y和t,描述一条对应的水管。x和y表示水管两端结点的编号,t表示准备送水所需要的时间。我们不妨为结点从1至N编号,这样所有的x和y都在范围[1, N]内。
以下Q行,每行描述一项任务。其中第一个整数为k:若k=1则后跟两个整数A和B,表示你需要为供水公司寻找一条满足要求的从A到B的水管路径;若k=2,则后跟两个整数x和y,表示直接连接x和y的水管宣布报废(保证合法,即在此之前直接连接x和y尚未报废的水管一定存在)。
 

Output

按顺序对应输入文件中每一项k=1的任务,你需要输出一个数字和一个回车/换行符。该数字表示:你寻找到的水管路径中所有管道全都完成准备工作所需要的时间(当然要求最短)。
 将狼踩尽 神犇的题解

题意:给出一个无向图,边有权值。定义一条路径的长度为该路径所有边的最大值。两种操作:(1)询问u到v所有路径的长度的最小值;(2)删掉某条边。

思路:

(1)将每个边也转化成一个点,比如边(1,2),转化成(1,3),(3,2)两个边;那么边权看做3的点权;1和2的点权可以看做0;

(2)反着做。首先将删掉的边都删掉,询问倒着回答,那么就成了加边。

(3)显然,路径长度就是最小生成树的最大值。因此,首先,将没有删掉的边求一次最小生成树,将最小生成树的边建立动态树。

(4)对于查询比较简单直接查就行。

(5)对于添加边(u,v),若u与v之前没有连通,则该边直接加进去就行,其实就是两个子树连在一起;否则,该边加进去之后会出现环,那么需要删掉环上的最大值的边。

然后因为p[x]习惯性的写成了fa[x]然后调了一节晚自修我TAT

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
using namespace std;
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define clr(x,c) memset(x,c,sizeof(x))
int read(){
int x=0;char c=getchar();
while(!isdigit(c)) c=getchar();
while(isdigit(c)) x=x*10+c-'0',c=getchar();
return x;
}
const int nmax=1500005;
const int maxn=1000005;
const int inf=0x7f7f7f7f;
struct edge{
int u,v,d,id,f;
};
edge es[maxn],ns[100005];
bool cmp(edge a,edge b){
return a.d<b.d;
}
bool comp(edge a,edge b){
return a.id<b.id;
}
bool cp(edge a,edge b){
return a.u<b.u||a.u==b.u&&a.v<b.v;
}
int fa[nmax],c[nmax][2],q[nmax],mx[nmax],w[nmax],ans[nmax],p[nmax];bool rev[nmax];
bool isroot(int x){
return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;
}
void pushup(int x){
int l=c[x][0],r=c[x][1];mx[x]=x;
if(w[mx[x]]<w[mx[l]]) mx[x]=mx[l];
if(w[mx[x]]<w[mx[r]]) mx[x]=mx[r];
}
void pushdown(int x){
if(rev[x]){
rev[x]=0;rev[c[x][0]]^=1;rev[c[x][1]]^=1;
swap(c[x][0],c[x][1]);
}
}
void rotate(int x){
int y=fa[x],z=fa[y],l=c[y][1]==x,r=l^1;
if(!isroot(y)) c[z][c[z][1]==y]=x;
fa[x]=z;fa[y]=x;fa[c[x][r]]=y;
c[y][l]=c[x][r];c[x][r]=y;
pushup(y);pushup(x);
}
void splay(int x){
int cnt=0,y,z;q[++cnt]=x;
for(int i=x;!isroot(i);i=fa[i]) q[++cnt]=fa[i];
dwn(i,cnt,1) pushdown(q[i]);
while(!isroot(x)){
y=fa[x];z=fa[y];
if(!isroot(y)){
if(c[y][0]==x^c[z][0]==y) rotate(x);
else rotate(y);
}
rotate(x);
}
}
void access(int x){
int t=0;
while(x) splay(x),c[x][1]=t,pushup(x),t=x,x=fa[x];
}
void rever(int x){
access(x);splay(x);rev[x]^=1;
}
void link(int x,int y){
rever(x);fa[x]=y;
}
void cut(int x,int y){
rever(x);access(y);splay(y);c[y][0]=fa[x]=0;
}
int query(int x,int y){
rever(x);access(y);splay(y);return mx[y];
}
int find(int x,int y,int m){
int l=1,r=m,mid;
while(l<=r){
mid=(l+r)>>1;
if(es[mid].u<x||es[mid].u==x&&es[mid].v<y) l=mid+1;
else if(es[mid].u==x&&es[mid].v==y) return mid;
else r=mid-1;
}
}
int ff(int x){
return p[x]==x?x:p[x]=ff(p[x]);
}
int main(){
int n=read(),m=read(),q=read(),tmp,temp,u,v,d,ta,tb;
rep(i,1,m) {
es[i].u=read(),es[i].v=read(),es[i].d=read();
if(es[i].u>es[i].v) swap(es[i].u,es[i].v);
}
sort(es+1,es+m+1,cmp);
rep(i,1,m) es[i].id=i,w[n+i]=es[i].d,mx[n+i]=n+i;
sort(es+1,es+m+1,cp);
rep(i,1,q) {
ns[i].d=read(),ns[i].u=read(),ns[i].v=read();
if(ns[i].u>ns[i].v) swap(ns[i].u,ns[i].v);
if(ns[i].d==2) tmp=find(ns[i].u,ns[i].v,m),es[tmp].f=1,ns[i].id=es[tmp].id;
}
sort(es+1,es+m+1,comp);
int cnt=0;rep(i,1,n) p[i]=i;
rep(i,1,m) if(!es[i].f){
u=es[i].u;v=es[i].v;ta=ff(u);tb=ff(v);
if(ta!=tb){
p[ta]=tb;link(u,i+n);link(v,i+n);//因为这里的p习惯性的打成了fa调了一节晚自修各种中间输出就是在这里WA然后一直检查模版TAT
if(++cnt==n-1) break;
}
}
dwn(i,q,1){
if(ns[i].d==1) ans[i]=w[query(ns[i].u,ns[i].v)];
else{
u=ns[i].u,v=ns[i].v,d=ns[i].id;tmp=query(u,v);
if(es[d].d<w[tmp]) {
cut(tmp,es[tmp-n].u);cut(tmp,es[tmp-n].v);
link(u,d+n);link(v,d+n);
}
}
}
rep(i,1,q) if(ns[i].d==1) printf("%d\n",ans[i]);
return 0;
}

  

link cut tree 入门的更多相关文章

  1. bzoj2049 &lbrack;Sdoi2008&rsqb;Cave 洞穴勘测 link cut tree入门

    link cut tree入门题 首先说明本人只会写自底向上的数组版(都说了不写指针.不写自顶向下QAQ……) 突然发现link cut tree不难写... 说一下各个函数作用: bool isro ...

  2. Codeforces Round &num;339 &lpar;Div&period; 2&rpar; A&period; Link&sol;Cut Tree 水题

    A. Link/Cut Tree 题目连接: http://www.codeforces.com/contest/614/problem/A Description Programmer Rostis ...

  3. Link&sol;cut Tree

    Link/cut Tree 一棵link/cut tree是一种用以表示一个森林,一个有根树集合的数据结构.它提供以下操作: 向森林中加入一棵只有一个点的树. 将一个点及其子树从其所在的树上断开. 将 ...

  4. 洛谷P3690 Link Cut Tree &lpar;模板&rpar;

    Link Cut Tree 刚开始写了个指针版..调了一天然后放弃了.. 最后还是学了黄学长的板子!! #include <bits/stdc++.h> #define INF 0x3f3 ...

  5. LCT总结——概念篇&plus;洛谷P3690&lbrack;模板&rsqb;Link Cut Tree&lpar;动态树&rpar;(LCT,Splay)

    为了优化体验(其实是强迫症),蒟蒻把总结拆成了两篇,方便不同学习阶段的Dalao们切换. LCT总结--应用篇戳这里 概念.性质简述 首先介绍一下链剖分的概念(感谢laofu的讲课) 链剖分,是指一类 ...

  6. P3690 【模板】Link Cut Tree (动态树)

    P3690 [模板]Link Cut Tree (动态树) 认父不认子的lct 注意:不 要 把 $fa[x]$和$nrt(x)$ 混 在 一 起 ! #include<cstdio> v ...

  7. Link Cut Tree学习笔记

    从这里开始 动态树问题和Link Cut Tree 一些定义 access操作 换根操作 link和cut操作 时间复杂度证明 Link Cut Tree维护链上信息 Link Cut Tree维护子 ...

  8. &lbrack;CodeForces - 614A&rsqb; A - Link&sol;Cut Tree

    A - Link/Cut Tree Programmer Rostislav got seriously interested in the Link/Cut Tree data structure, ...

  9. Link Cut Tree 总结

    Link-Cut-Tree Tags:数据结构 ##更好阅读体验:https://www.zybuluo.com/xzyxzy/note/1027479 一.概述 \(LCT\),动态树的一种,又可以 ...

随机推荐

  1. oracle分页查询sql

    select * from( select shopid,rownum rn from p_shopinfo where is_hot=1 and rownum <=6 order by sho ...

  2. 让Sqlite脱离VC&plus;&plus; Runtime独立运行

    前段时间在开发OrayTalk(傲瑞通)的聊天记录模块时用到了Sqlite,这是我第一次接触和使用Sqlite,总体感觉还是非常不错的.这里把我使用Sqlite的经验跟大家分享一下. 一.关于Sqli ...

  3. SQL Server优化之SQL语句优化

    一切都是为了性能,一切都是为了业务 一.查询的逻辑执行顺序 (1) FROM left_table (3) join_type JOIN right_table (2) ON join_conditi ...

  4. win10 pro eclipse maven: Cannot read lifecycle mapping metadata for artifact org&period;apache&period;maven&period;plugins&colon;mav invalid END header &lpar;bad central directory offset&rpar;

    Error:Cannot read lifecycle mapping metadata for artifact org.apache.maven.plugins:mav ... invalid E ...

  5. Unity重要的函数

    Awake 当一个脚本实例被载入时Awake被调用. Start Start仅在Update函数第一次被调用前调用. Update 当MonoBehaviour启用时,其Update在每一帧被调用. ...

  6. &lbrack;黑马程序员&rsqb; 集合框架2——Map系 &amp&semi; 集合工具类&lpar;Collections、Arrays&rpar;

    ---------------------- ASP.Net+Android+IO开发..Net培训.期待与您交流! ---------------------- 0. 集合框架按其所实现的接口, 大 ...

  7. linux进阶命令第一天

    1.history -c 清空历史命令     保存的目录 vim ~/.bash_history history -w 立即把内存中的数据写入历史文件中 vim /etc/profile 默认配置文 ...

  8. 前端必须掌握的30个CSS选择器

    也许你已经学会了CSS的三个简单常用的选择器:#ID,.class,标签选择器,可是这些就足够了吗?随着CSS3的到来,作为前端开发者需要掌握下面三十个基本的选择器,这样才可以在平时开发中得心用手. ...

  9. 在 Android studio 中 配置Gradle 进行 &OpenCurlyDoubleQuote;动态编译期间,指定 远程服务器地址 ,生成多个安装包”

    需求: 在产品开发中,经常需要发布各个版本,每个版本的服务器地址有不同的服务器地址.比如 开发服务器使用 192.168.1.232服务器, 测试服务器使用 192.168.1.245服务器, 正式上 ...

  10. BZOJ 2876 【NOI2012】 骑行川藏

    题目链接:骑行川藏 听说这道题需要一些高数知识 于是膜了一发dalao的题解……然后就没了…… 不要吐槽我的精度TAT……eps设太小了就TLE,大了就Wa……我二分的边界是对着数据卡的…… 下面贴代 ...