给出一张 n ( n ≤ 10000 ) n(n\le 10000) n(n≤10000)个点, m ( m ≤ 100000 ) m(m\le 100000) m(m≤100000)条边的无向图,每条边有一种颜色,一共有 C ( C < 10 ) C(C<10) C(C<10)种颜色,每个点有一个权值。这个图满足以下两种情况:
1. 对于任意节点连出去的边中,相同颜色的边不超过两条。
2. 图中不存在同色的环,同色的环指相同颜色的边构成的环。
一共会进行 q ( q ≤ 100000 ) q(q\le 100000) q(q≤100000)种操作,每次操作都是下列 3 3 3个之中一个:
0. 修改一个点的权值;
1. 修改一条边的颜色;
2. 查询由颜色 c c c的边构成的图中,所有可能在节点 u u u到节点 v v v之间的简单路径上的节点的权值的最大值。
我们发现他给定的性质表明同一种颜色的边所构成的图肯定是一个森林,那么我们可以考虑每个森林用一颗LCT来维护,对于修改边的操作,如果可以修改,那么我们将这条边从原颜色的LCT上删去,加入新颜色的LCT上,剩下就是基本操作了。
#pragma GCC optimize(3,"Ofast","inline")
#include
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);
}
}
}