bzoj 2594 水管局长 | LCT | 最小生成树

将所有操作逆序,删边就成了加边,做LCT维护最小生成树即可。
把边i表示成点i+n,那么加一条边x,y就相当于link(x,i+n) link(y,i+n) 删边同理。
我的release写成了“上放标记”了 TAT

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<cmath>

#define md
#define ll long long
#define inf (int) 1e9
#define eps 1e-8
#define N 2000010
using namespace std;
struct yts { int x,y,l,id,del;} e[N];
struct QQ { int x,y,pos,opt;} q[200010];
int father[N];
int n,m;
int ch[N][2],fa[N],rev[N],mx[N],po[N],dt[N],que[N];
int ans[100010];

int read()
{
char ch = getchar();
for ( ; ch > '9' || ch < '0'; ch = getchar());
int tmp = 0;
for ( ; '0' <= ch && ch <= '9'; ch = getchar())
tmp = tmp * 10 + int(ch) - 48;
return tmp;
}
bool cmpid(yts a,yts b) { return a.id<b.id;}
bool cmp(yts a,yts b) { return a.l<b.l; }
bool operator < (yts a,yts b) { return a.x==b.x?a.y<b.y:a.x<b.x; }
int find_path(int x,int y)
{
yts nd; nd.x=x; nd.y=y;
int l=1,r=m;
while (l!=r)
{
int mid=(l+r)>>1;
if (e[mid]<nd) l=mid+1; else r=mid;
}
return l;
}
int find(int x) { return father[x]==x?x:father[x]=find(father[x]);}
//-----------------------------------------------------------------

bool isroot(int x) { if (ch[fa[x]][0]==x||ch[fa[x]][1]==x) return 0; else return 1; }
bool dir(int x) { return ch[fa[x]][1]==x;}
void release(int i)
{
if (rev[i])
{
rev[i]=0;
swap(ch[i][0],ch[i][1]);
if (ch[i][0]) rev[ch[i][0]]^=1;
if (ch[i][1]) rev[ch[i][1]]^=1;
}
}

void update(int i)
{
int l=ch[i][0],r=ch[i][1];
mx[i]=dt[i]; po[i]=i;
if (l&&mx[i]<mx[l]) { mx[i]=mx[l]; po[i]=po[l]; }
if (r&&mx[i]<mx[r]) { mx[i]=mx[r]; po[i]=po[r]; }
}

void rotate(int x)
{
int y=fa[x],z=fa[y],b=dir(x),c=dir(y),a=ch[x][!b];
if (!isroot(y)) ch[z][c]=x;
if (a) fa[a]=y; fa[y]=x; fa[x]=z;
ch[x][!b]=y; ch[y][b]=a;
update(y); update(x);
}

void splay(int x)
{
int w=0; que[++w]=x;
for (int i=x;!isroot(i);i=fa[i]) que[++w]=fa[i];
for (int i=w;i;i--) release(que[i]);
while (!isroot(x))
{
int y=fa[x],z=fa[y];
if (isroot(y)) rotate(x);
else
{
if (dir(x)==dir(y)) { rotate(y); rotate(x); }
else { rotate(x); rotate(x); }
}
}
}

void access(int x)
{
int t=0;
while (x)
{
splay(x);
ch[x][1]=t;
update(x);
t=x; x=fa[x];
}
}

void makeroot(int x)
{
access(x); splay(x); rev[x]^=1;
}

void link(int x,int y)
{
makeroot(x); fa[x]=y;
}

void cut(int x,int y)
{
makeroot(x); access(y); splay(y);
fa[x]=ch[y][0]=0;
update(x); update(y);
}

int query(int x,int y)
{
makeroot(x); access(y); splay(y);
return po[y];
}


//-----------------------------------------------------------------
int main()
{
//freopen("data.in","r",stdin); freopen("data.out","w",stdout);
int Q;
n=read(),m=read(),Q=read();
for (int i=1;i<=n;i++) dt[i]=mx[i]=0;
for (int i=1;i<=m;i++)
{
e[i].x=read(); e[i].y=read(); e[i].l=read();
if (e[i].x>e[i].y) swap(e[i].x,e[i].y);
e[i].id=i; dt[i+n]=mx[i+n]=e[i].l;
}
sort(e+1,e+m+1);
for (int i=1;i<=Q;i++)
{
int opt=read(),x=read(),y=read();
if (x>y) swap(x,y);
if (opt==1)
{
q[i].x=x; q[i].y=y; q[i].opt=1;
}
else
{
int p=find_path(x,y);
e[p].del=1;
q[i].x=x; q[i].y=y; q[i].opt=2; q[i].pos=e[p].id;
}
}
sort(e+1,e+m+1,cmp);
for (int i=1;i<=n;i++) father[i]=i;
for (int i=1;i<=m;i++)
{
if (e[i].del) continue;
int x=e[i].x,y=e[i].y;
int f1=find(x),f2=find(y);
if (f1!=f2)
{
father[f1]=f2;
int p=e[i].id;
link(x,p+n); link(y,p+n);
}
}
sort(e+1,e+m+1,cmpid);
int ww=0;
for (int i=Q;i;i--)
{
if (q[i].opt==1) ans[++ww]=dt[query(q[i].x,q[i].y)];
else
{
int id=q[i].pos,x=q[i].x,y=q[i].y;
int p=query(x,y);
if (dt[p]>e[id].l)
{
cut(x,p); cut(y,p);
link(x,id+n); link(y,id+n);
}
}
}
for (int i=ww;i;i--) printf("%d\n",ans[i]);
return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值