[luogu2173][ZJOI2012]网络(LCT)

题意

  给出一张 n ( n ≤ 10000 ) n(n\le 10000) n(n10000)个点, m ( m ≤ 100000 ) m(m\le 100000) m(m100000)条边的无向图,每条边有一种颜色,一共有 C ( C &lt; 10 ) C(C&lt;10) C(C<10)种颜色,每个点有一个权值。这个图满足以下两种情况:
    1. 对于任意节点连出去的边中,相同颜色的边不超过两条。
    2. 图中不存在同色的环,同色的环指相同颜色的边构成的环。
  一共会进行 q ( q ≤ 100000 ) q(q\le 100000) q(q100000)种操作,每次操作都是下列 3 3 3个之中一个:
    0. 修改一个点的权值;
    1. 修改一条边的颜色;
    2. 查询由颜色 c c c的边构成的图中,所有可能在节点 u u u到节点 v v v之间的简单路径上的节点的权值的最大值。

分析

  我们发现他给定的性质表明同一种颜色的边所构成的图肯定是一个森林,那么我们可以考虑每个森林用一颗LCT来维护,对于修改边的操作,如果可以修改,那么我们将这条边从原颜色的LCT上删去,加入新颜色的LCT上,剩下就是基本操作了。

Code

#pragma GCC optimize(3,"Ofast","inline")
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
bool Finish_read;
template<class T>inline void read(T &x){Finish_read=0;x=0;int f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-')f=-1;if(ch==EOF)return;ch=getchar();}while(isdigit(ch))x=x*10+ch-'0',ch=getchar();x*=f;Finish_read=1;}
template<class T>inline void print(T x){if(x/10!=0)print(x/10);putchar(x%10+'0');}
template<class T>inline void writeln(T x){if(x<0)putchar('-');x=abs(x);print(x);putchar('\n');}
template<class T>inline void write(T x){if(x<0)putchar('-');x=abs(x);print(x);}
/*================Header Template==============*/
const int maxn=10005;
struct LCT {
	int col[maxn];
	struct Node {
		int ch[2],fa,tag,val,mx;
		inline int& operator [] (const int &x) {
			return this->ch[x];
		}
	}T[maxn];
	inline int Max(int x) {
		return x?T[x].mx:0;
	}
	inline void Reverse(int x) {
		if(x)
			swap(T[x][0],T[x][1]),T[x].tag^=1;
	}
	inline void Pushup(int x) {
		T[x].mx=max(T[x].val,max(Max(T[x][0]),Max(T[x][1])));
	}
	inline void Pushdown(int x) {
		if(T[x].tag)
			Reverse(T[x][0]),Reverse(T[x][1]),T[x].tag=0;
	}
	inline int Isroot(int x) {
		return T[T[x].fa][0]!=x&&T[T[x].fa][1]!=x;
	}
	inline int Isright(int x) {
		return T[T[x].fa][1]==x;
	}
	inline void Pushtag(int x) {
		if(!Isroot(x))
			Pushtag(T[x].fa);
		Pushdown(x);
	}
	inline void Rotate(int x) {
		int y=T[x].fa,z=T[y].fa,r=Isright(x),l=r^1;
		if(!Isroot(y))
			T[z][Isright(y)]=x;
		T[x].fa=z,T[T[x][l]].fa=y,T[y][r]=T[x][l],T[x][l]=y,T[y].fa=x,Pushup(y),Pushup(x);
	}
	inline void Splay(int x) {
		Pushtag(x);
		for(int y;!Isroot(x);Rotate(x))
			if(!Isroot(y=T[x].fa))
				Rotate(Isright(x)^Isright(y)?x:y);
	}
	inline void Access(int x) {
		for(int y=0;x;x=T[y=x].fa)
			Splay(x),T[x][1]=y,Pushup(x);
	}
	inline void Makeroot(int x) {
		Access(x),Splay(x),Reverse(x);
	}
	inline int Findroot(int x) {
		Access(x),Splay(x);
		for(;T[x][0];x=T[x][0]);
		return Splay(x),x;
	}
	inline void Findpath(int x,int y) {
		Makeroot(x),Access(y),Splay(y);
	}
	inline int Link(int x,int y) {
		Makeroot(x);
		if(Findroot(y)==x)
			return 0;
		return T[x].fa=y,1;
	}
	inline void Cut(int x,int y) {
		Makeroot(x);
		if(Findroot(y)!=x)
			return;
		Splay(y);
		if(T[y][0]!=x)
			return;
		T[x].fa=T[y][0]=0,Pushup(y);
	}
}L[10];
int n,m,C,k;
map<int,int>id[maxn];
struct Edge {
	int u,v,c;
}E[maxn*10];
int main() {
	read(n),read(m),read(C),read(k);
	for(int i=1,x;i<=n;++i) {
		read(x);
		for(int j=0;j<C;++j)
			L[j].T[i].val=x;
	}
	for(int i=1,u,v,w,c;i<=m;++i) {
		read(u),read(v),read(w);
		if(u>v)
			swap(u,v);
		id[u][v]=i,E[i]=(Edge){u,v,w},c=L[w].Link(u,v),L[w].col[u]++,L[w].col[v]++,assert(c);
	}
	for(int ty,a,b,c;k--;) {
		read(ty),read(a),read(b);
		if(ty==0)
			for(int i=0;i<C;++i)
				L[i].Makeroot(a),L[i].T[a].val=b,L[i].Pushup(a);
		if(ty==1) {
			read(c);
			if(a>b)
				swap(a,b);
			if(!id[a].count(b)) {
				puts("No such edge.");
				continue;
			}
			int p=id[a][b],d=E[p].c;
			if(c==d) {
				puts("Success.");
				continue;
			}
			if(L[c].col[a]==2||L[c].col[b]==2) {
				puts("Error 1.");
				continue;
			}
			int op=L[c].Link(a,b);
			puts(op?"Success.":"Error 2.");
			if(op)
				L[d].col[a]--,L[d].col[b]--,L[c].col[a]++,L[c].col[b]++,E[p].c=c,L[d].Cut(a,b);
		}
		if(ty==2) {
			read(c);
			L[a].Makeroot(b);
			if(L[a].Findroot(c)!=b)
				puts("-1");
			else
				L[a].Findpath(b,c),printf("%d\n",L[a].T[c].mx);
		}
	}
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值