这是一篇来自另一条世界线的总结(昨晚我明明写完了,也交上去了,然而今天不见了)。
题意就是给你n个点,三个操作:
①将点A,B连一条无向边
②改变点A的权值
③问你从A到B的路径的权值和,其中边可以重复一个方向走,点可以重复走但权值只算一次。
由于是一个无向图,可能会有环,根据题意,环上的点是可以全部走完,而且是最优的,故可以缩点。缩完之后剩下一棵树。
倘若只有③,LCA就可以解决了,有了②,树链剖分也是妥妥的(如果不卡常的话),而有了①,就考虑LCT吧。
倘若加边出现环,就要进行缩点了。我们可以把环扔到一棵Splay上,把这棵Splay的和都扔在一个点上,然后把这棵Splay废掉,只留下一个点来代表这个环。废掉的时候记录这个点的信息最终扔到了哪个点上。
在Access跳Pathparent时直接跳到真正的点上就可以了。
(在这里我迷了一晚,找Ever请教了…次,所以大家去膜他的题解吧)
如果代码写得长长长,也许常数就会小一丢丢了,也许就可以不用并查集代替Findroot来判连通性了。
#include <iostream>
#include <fstream>
#include <algorithm>
#include <cmath>
#include <ctime>
#include <cstdio>
#include <cstdlib>
#include <cstring>
using namespace std;
#define mmst(a, b) memset(a, b, sizeof(a))
#define mmcp(a, b) memcpy(a, b, sizeof(b))
typedef long long LL;
const int N=200200;
int biu[N],n,m,cnt;
struct tree
{
tree *c[2],*f,*pp,*now;
int num,sum,a,flip;
int d(){return f->c[1]==this;}
void sc(tree *x,int d){(c[d]=x)->f=this;}
tree *truth()
{ while(now!=now->now)
now=now->now;
return now;
}
}nil[N],*ro[N];
tree *newtree(int k)
{
nil[++cnt]=nil[0];
nil[cnt].sum=nil[cnt].a=k;
return nil+cnt;
}
void up(tree *x)
{
x->sum=x->a+x->c[0]->sum+x->c[1]->sum;
}
void down(tree *x)
{
if(!x->flip)
return;
x->flip=0;
swap(x->c[0],x->c[1]);
if(x->c[0]!=nil) x->c[0]->flip^=1;
if(x->c[1]!=nil) x->c[1]->flip^=1;
}
void work(tree *x)
{
if(x->f!=nil)
work(x->f);
down(x);
}
void zig(tree *x)
{
tree *y=x->f;
int d=x->d();
y->sc(x->c[!d],d);
if(y->f==nil)
x->f=nil;
else
y->f->sc(x,y->d());
x->sc(y,!d);
up(y);
up(x);
x->pp=y->pp;
y->pp=nil;
}
void splay(tree *x)
{
work(x);
for(tree *y;x->f!=nil;)
{
y=x->f;
if(y->f!=nil)
(x->d() ^ y->d()) ? zig(x) : zig(y);
zig(x);
}
}
void Access(tree *x)
{
tree *y=nil;
while(x!=nil)
{
splay(x);
if(x->c[1]!=nil)
{
x->c[1]->f=nil;
x->c[1]->pp=x;
}
x->c[1]=y;
if(y!=nil)
y->f=x;
up(x);
y->pp=nil;
y=x;
x=x->pp->truth();
}
}
void Evert(tree *x)
{
Access(x);
splay(x);
x->flip^=1;
}
tree *Findro(tree *x)
{
Access(x);
splay(x);
while(x->c[0]!=nil)
{
x=x->c[0];
down(x);
}
splay(x);
return x;
}
void Link(tree *x,tree *y)
{
Evert(x);
x->pp=y;
}
void unite(tree *x,tree *y)
{
if(x!=y)
{
y->a+=x->a;
x->a=x->sum=0;
x->now=y;
ro[x->num]=ro[y->num];
}
if(x->c[0]!=nil)
unite(x->c[0],y);
if(x->c[1]!=nil)
unite(x->c[1],y);
}
int main()
{
nil->c[0]=nil->c[1]=nil->f=nil->pp=nil->now=nil;
cin>>n>>m;
for(int i=1;i<=n;i++)
{
scanf("%d",&biu[i]);
ro[i]=newtree(biu[i]);
ro[i]->now=ro[i];
ro[i]->num=i;
}
for(int i=1;i<=m;i++)
{
int ops,x,y;
scanf("%d%d%d",&ops,&x,&y);
if(ops==1)
{
tree *X=ro[x]->truth();
tree *Y=ro[y]->truth();
if(Findro(ro[x])==Findro(ro[y]))
{
Evert(X);
Access(Y);
splay(X);
unite(X,X);
X->c[0]->f=nil;
X->c[0]=nil;
up(X);
}
else
Link(X,Y);
}
if(ops==2)
{
tree *X=ro[x]->truth();
splay(X);
X->a+=y-biu[x];
up(X);
biu[x]=y;
}
if(ops==3)
{
tree *X=ro[x]->truth();
tree *Y=ro[y]->truth();
if(Findro(ro[x])==Findro(ro[y]))
{
Evert(Y);
Access(X);
splay(X);
printf("%d\n",X->sum);
}
else
printf("-1\n");
}
}
return 0;
}