BZOJ 2594: [Wc2006]水管局长数据加强版(lct)
解题思路
一道\(lct\)维护动态最小生成树。刚开始写了一遍疯狂\(Re\),冷静了一下重新写了一遍终于过了。首先题目中要求两点之间最大值的最小值,其实就是维护一个最小生成树,每次询问最大值。要将删边转化成加边操作,就是倒着处理,这里用\(set\)和\(map\)就比较方便。然后还要把边权转化成点权,具体来说就是假如\(x,y\)直接有一条权值为\(z\)的边,那么就新开一个节点,令这个点的权值为\(z\),然后连接\(x\)与这个节点,\(y\)与这个节点。\(lct\)里维护一下最大值的节点的编号。每当假如一条边时要判断一下这条路径的最大值是否大于新加入的边,大于的话就先\(cut\)掉最大值,然后再\(link\)。
代码
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<algorithm>
#include<map>
#include<set>
using namespace std;
const int MAXN = 100005;
const int MAXM = 1000005;
inline int rd(){
int x=0,f=1;char ch=getchar();
while(!isdigit(ch)) {f=ch=='-'?0:1;ch=getchar();}
while(isdigit(ch)) {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}
return f?x:-x;
}
int n,m,q,num,fa[MAXN+MAXM],ch[MAXN+MAXM][2],Max[MAXN+MAXM];
int F[MAXN],val[MAXN+MAXM],tot,xx[MAXN+MAXM],yy[MAXN+MAXM];
int cnt,ans[MAXN];
bool tag[MAXN+MAXM];
map<pair<int,int>,int > mp;
set<pair<int,int> > S;
struct Edge{
int x,y,z,id;
friend bool operator<(const Edge A,const Edge B){
return A.z<B.z;
}
}edge[MAXM];
struct Query{
int x,y,type,id;
}ask[MAXN];
int Find(int x){
if(x==F[x]) return x;
return F[x]=Find(F[x]);
}
inline bool check(int x){
return (x==ch[fa[x]][1]);
}
inline bool isroot(int x){
return (x!=ch[fa[x]][0] && x!=ch[fa[x]][1]);
}
inline void pushup(int x){
Max[x]=x;
if(val[Max[ch[x][0]]]>val[Max[x]]) Max[x]=Max[ch[x][0]];
if(val[Max[ch[x][1]]]>val[Max[x]]) Max[x]=Max[ch[x][1]];
}
inline void pushdown(int x){
if(tag[x]){
tag[x]=0;swap(ch[x][0],ch[x][1]);
if(ch[x][0]) tag[ch[x][0]]^=1;
if(ch[x][1]) tag[ch[x][1]]^=1;
}
}
void pd(int x){
if(!isroot(x)) pd(fa[x]);pushdown(x);
}
inline void rotate(int x){
int y=fa[x],z=fa[y];bool chk=check(x);
if(!isroot(y)) ch[z][check(y)]=x;
ch[y][chk]=ch[x][chk^1];fa[ch[x][chk^1]]=y;
ch[x][chk^1]=y;fa[y]=x;fa[x]=z;pushup(y);pushup(x);
}
inline void splay(int x){
pd(x);
for(;!isroot(x);rotate(x))
if(!isroot(fa[x])) rotate(check(fa[x])==check(x)?fa[x]:x);
}
inline void access(int x){
for(int y=0;x;y=x,x=fa[x]){
splay(x);ch[x][1]=y;pushup(x);
}
}
inline void makeroot(int x){
access(x);splay(x);tag[x]^=1;
}
inline void link(int x,int y){
makeroot(x);fa[x]=y;pushup(y);
}
inline void cut(int x,int y){
makeroot(x);access(y);splay(y);
ch[y][0]=fa[x]=0;pushup(y);
}
inline void split(int x,int y){
makeroot(x);access(y);splay(x);
}
int main(){
n=rd(),m=rd(),q=rd();int x,y,z;num=n;
for(int i=1;i<=n;i++) F[i]=i;
for(int i=1;i<=m;i++) {
x=rd(),y=rd(),z=rd();if(x>y) swap(x,y);
edge[i].x=x,edge[i].y=y,edge[i].z=z;
mp[make_pair(x,y)]=z;
}
for(int i=1;i<=q;i++){
z=rd(),x=rd(),y=rd();if(x>y) swap(x,y);
if(z==2) S.insert(make_pair(x,y));
ask[i].type=z;ask[i].x=x;ask[i].y=y;
}
sort(edge+1,edge+1+m);int u,v;
for(int i=1;i<=m;i++){
x=edge[i].x,y=edge[i].y;
if(S.find(make_pair(x,y))!=S.end()) continue;
u=Find(x);v=Find(y);if(u==v) continue;
F[u]=v;val[++num]=edge[i].z;link(x,num);link(num,y);
xx[num]=x;yy[num]=y;tot++;if(tot==n-1) break;
}int x1,x2;
for(int i=q;i;i--){
x=ask[i].x;y=ask[i].y;
if(ask[i].type==1) {split(x,y);ans[++cnt]=val[Max[x]];}
else{
val[++num]=mp[make_pair(x,y)];
split(x,y);if(val[Max[x]]<=val[num]) continue;
x1=xx[Max[x]];x2=Max[x]; //!!!注意这里要把值拿出来,否则第一个$cut$的时候有可能改变这里的值。
cut(xx[Max[x]],Max[x]);cut(x2,x1);
link(x,num);link(num,y);xx[num]=x;yy[num]=y;
}
}
for(int i=cnt;i;i--) printf("%d\n",ans[i]);
return 0;
}