传送门
这是一道函数很杂的LCT题。(有点类似Qtree6,Qtree7。但略难。)
思路:
c那么小,直接建c棵树。
处理方式:
- 更改每棵树上该点的值。
- 最难点。先找到边的颜色(一颗一颗找),如果找不到,输出“No such edge.”。
如果边的颜色与更改后的相同,输出“Success.”。
用 ,就可以方便地判“Error 1.”
设变化后的颜色为w,若在对应的树上两个点已经联通,则输出“Error 2.“
最后,删边建边,输出“Success."就行。 - splay节点加一个mx参数即可。
代码:
#include<cstdio>
#include<cctype>
#include<cstring>
#include<algorithm>
#define g g()
#define lc tr[x].son[0]
#define rc tr[x].son[1]
using namespace std;
const int N=1e4+10,inf=0x7fffffff,size=1<<25;
int d[N];struct node{int f,son[2],mx;bool v;};
int n,m,c,k;char cnt[N][12];
struct Link_Cut_Tree{
node tr[N];
void update(int x){tr[x].mx=max(d[x],max(tr[lc].mx,tr[rc].mx));}
void fz(int x){//翻转
tr[x].v=0;swap(lc,rc);
tr[lc].v^=1;tr[rc].v^=1;
}
bool rt(int x){return tr[tr[x].f].son[0]!=x&&tr[tr[x].f].son[1]!=x;}//是否为根
void dfs(int x){//递归维护
if(!rt(x))dfs(tr[x].f);
if(tr[x].v)fz(x);
}
void rotate(int x,int w){//旋转
int f=tr[x].f,ff=tr[f].f,r,R;
r=tr[x].son[w];R=f;tr[R].son[1-w]=r;if(r)tr[r].f=R;
r=x;R=ff;if(tr[R].son[0]==f)tr[R].son[0]=r;else if(tr[R].son[1]==f)tr[R].son[1]=r; tr[r].f=R;
r=f;R=x;tr[R].son[w]=r;tr[r].f=R;update(f);update(x);
}
void splay(int x){
dfs(x);
while(!rt(x)){
int f=tr[x].f;
if(rt(f))rotate(x,tr[f].son[0]==x);
else{
int ff=tr[f].f,a=(tr[f].son[0]==x),b=(tr[ff].son[0]==f);
if(a^b)rotate(x,a),rotate(x,b);
else rotate(f,a),rotate(x,a);
}
}
}
void access(int x){for(int y=0;x;x=tr[y=x].f)splay(x),rc=y,update(x);}
void makeroot(int x){access(x);splay(x);tr[x].v^=1;}
int find_root(int x){access(x);splay(x);while(lc)x=lc;return x;}
void split(int x,int y){makeroot(y);access(x);splay(x);}
bool connect(int x,int y){split(x,y);return lc==y&&(!tr[y].son[1]);}//x,y是否有边连接
bool unicom(int x,int y){split(x,y);while(lc)x=lc;return x==y;}//判联通
void link(int x,int y){makeroot(x);tr[x].f=y;access(x);}
void cut(int x,int y){split(x,y);lc=0;tr[y].f=0;update(x);}
int query(int x,int y){return unicom(x,y)?tr[x].mx:-1;}
}lct[12];
//0
void change(int x,int w){
d[x]=w;
for(int i=0;i<c;i++)lct[i].access(x),lct[i].splay(x),lct[i].update(x);
}
//1
void color(int x,int y,int w){
int z=-1;
for(int i=0;i<c;i++)if(lct[i].connect(x,y)){z=i;break;}//有了splay,用什么map查边(常数那么大)
if(z==w){puts("Success.");return;}//坑点
if(z<0){puts("No such edge.");return;}
if((cnt[x][w]>=2||cnt[y][w]>=2)){puts("Error 1.");return;}//
if(lct[w].unicom(x,y)){puts("Error 2.");return;}
lct[z].cut(x,y); cnt[x][z]--;cnt[y][z]--;
lct[w].link(x,y);cnt[x][w]++;cnt[y][w]++;
puts("Success.");
}
//2
int ask(int w,int x,int y){return lct[w].query(x,y);}
char buf[size],*p1=buf,*p2=buf;
char g{return p1==p2&&(p2=(p1=buf)+fread(buf,1,size,stdin),p1==p2)?EOF:*p1++;}
void qr(int &x){
char c=g;bool v=x=0;
while(!(isdigit(c)||c=='-'))c=g;
if(c=='-')v=1,c=g;
while(isdigit(c))x=x*10+c-'0',c=g;
if(v)x=-x;
}
void write(int x){
if(x/10)write(x/10);
putchar(x%10+'0');
}
void pri(int x){
if(x<0)putchar('-'),x=-x;
write(x);puts("");
}
int main(){
qr(n);qr(m);qr(c);qr(k);
for(int j=0;j<c;j++)lct[j].tr[0].mx=-inf;//初始化
for(int i=1;i<=n;i++){
qr(d[i]);
for(int j=0;j<c;j++)lct[j].tr[i].mx=d[i];//初始化
}
int op,x,y,w;
while(m--)qr(x),qr(y),qr(w),lct[w].link(x,y),cnt[x][w]++,cnt[y][w]++;
while(k--){
qr(op);
switch(op){
case 0:qr(x);qr(y);change(x,y);break;
case 1:qr(x);qr(y);qr(w);color(x,y,w);break;
case 2:qr(w);qr(x);qr(y);pri(ask(w,x,y));break;
}
}
return 0;
}