cunzai_zsy0531

关注我

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;
}
posted @ 2022-04-21 20:22  cunzai_zsy0531  阅读(46)  评论(0编辑  收藏  举报