P6664 [清华集训2016]温暖会指引我们前行 题解
Post time: 2021-04-06 20:26:05
省选前写道LCT,rp++!
这道题和P4172水管局长有点像。需要维护一个动态最大生成树,然后对于每个询问求两点之间的边权和。维护动态最大生成树的话,就是维护一个边权 \(\min\) 和这条最小边权的边的编号。长度就维护一个边权和即可,这些都可以用LCT维护。
这道题比较妙的一点在于,把边当作一个新点,每次加边就是把这个新点和边的两个端点连边,这样很好维护信息,也很容易删除。
几个易错点:LCT板子里访问 \(0\) 节点;用左右儿子更新父亲答案的时候,没有判断左右儿子是否存在……反正我调了一个小时。
点击查看代码
#include<iostream>
#include<cstdio>
using namespace std;
inline void swap(int &x,int &y){x^=y^=x^=y;}
const int N=4e5+13,INF=0x3f3f3f3f;
struct edge{int u,v;}E[N];
int n,m,a[N],val[N],pos[N];
bool vis[N];
struct Stack{
int s[N],t;
inline void clear(){t=0;}
Stack(){clear();}
inline void push(int x){s[++t]=x;}
inline void pop(){--t;}
inline int top(){return s[t];}
inline bool empty(){return !t;}
};
struct Dsu{
int fa[N];
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}
inline bool check(int u,int v){return find(u)==find(v);}
inline void merge(int u,int v){
int x=find(u),y=find(v);
if(x!=y) fa[x]=y;
}
}tt;
struct Lct{
int fa[N],sum[N],ch[N][2],minval[N],minid[N];bool tag[N];
inline void refresh(int x){
sum[x]=sum[ch[x][0]]+sum[ch[x][1]]+a[x];
int v=val[x],d=pos[x];
//这个地方一定要注意,用儿子更新的时候,儿子不一定存在
if(ch[x][0]&&v>minval[ch[x][0]]) v=minval[ch[x][0]],d=minid[ch[x][0]];
if(ch[x][1]&&v>minval[ch[x][1]]) v=minval[ch[x][1]],d=minid[ch[x][1]];
minval[x]=v,minid[x]=d;
}
inline bool isroot(int x){return ch[fa[x]][0]!=x&&ch[fa[x]][1]!=x;}
inline bool chk(int x){return ch[fa[x]][1]==x;}
inline void rotate(int x){
int f=fa[x],gf=fa[f],k=chk(x),w=ch[x][k^1];
fa[x]=gf;if(!isroot(f)) ch[gf][chk(f)]=x;
if(w) fa[w]=f;ch[f][k]=w;
fa[f]=x;ch[x][k^1]=f;
refresh(f),refresh(x);
}
inline void pushdown(int x){
if(!tag[x]) return;
tag[ch[x][0]]^=1,tag[ch[x][1]]^=1,tag[x]=0;
swap(ch[x][0],ch[x][1]);
}
inline void splay(int x){
Stack st;int p=x;
while(!isroot(p)) st.push(p),p=fa[p];
st.push(p);
while(!st.empty()) pushdown(st.top()),st.pop();
while(!isroot(x)){
int f=fa[x];
if(!isroot(f)){
if(chk(f)==chk(x)) rotate(f);
else rotate(x);
}
rotate(x);
}
}
inline void access(int x){
for(int p=0;x;p=x,x=fa[x]) splay(x),ch[x][1]=p,refresh(x);
}
inline void makeroot(int x){access(x);splay(x);tag[x]^=1;}
inline int findroot(int x){
access(x);splay(x);
while(ch[x][0]) x=ch[x][0];
return x;
}
inline void split(int x,int y){makeroot(x);access(y);splay(y);}
inline void link(int x,int y){
makeroot(x);
if(findroot(y)!=x) fa[x]=y;
}
inline void cut(int x,int y){
split(x,y);
if(ch[y][0]==x&&!ch[x][1]) fa[x]=ch[y][0]=0;
}
}T;
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i) tt.fa[i]=i,val[i]=T.minval[i]=INF;
for(int i=1,id,u,v,t,l;i<=m;++i){
char op[10];
scanf("%s",op);
if(op[0]=='f'){//find id u v t l
scanf("%d%d%d%d%d",&id,&u,&v,&t,&l);++id,++u,++v;
E[id]=(edge){u,v};a[id+n]=l,val[id+n]=t,pos[id+n]=id;
if(tt.check(u,v)){
T.split(u,v);
if(t<=T.minval[v]) continue;//如果温度太低,就不会加入最大生成树,直接跳过
int p=T.minid[v];
T.cut(E[p].u,p+n),T.cut(E[p].v,p+n);
vis[p]=0;
}
else tt.merge(u,v);
T.link(u,id+n);
T.link(v,id+n),vis[id]=1;
}
else if(op[0]=='m'){//move u v
scanf("%d%d",&u,&v);++u,++v;
if(!tt.check(u,v)) puts("-1");
else T.split(u,v),printf("%d\n",T.sum[v]);
}
else{//change id l
scanf("%d%d",&id,&l);++id;
if(vis[id]){//如果这条边在最大生成树中,才调整
T.access(id+n);T.splay(id+n);
a[id+n]=l,T.refresh(id+n);
}
}
}
return 0;
}