题目大意:给定n个点,支持以下操作:
1.在某两个点之间连接一条无向边
2.改变某个点的权值
3.将每条边设定一个方向,然后从 x 走到 y ,求能经过的所有点的权值和
首先如果这个图是静态的,我们把边双都缩点,那么每次询问显然就是两个点所在边双路径上的点权和
现在图是动态的,因此我们用动态树维护一下就行了
如果连边的两个点不连通,就在LCT中连接这两个点
如果连边的两个点已经连通,就将这个两个点路径上的所有点用并查集缩点
时间复杂度 O(mlogn)
似乎用链剖会快一些?
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 150100
using namespace std;
int n,m;
int a[M];
struct Union_Find_Set{
int fa[M];
int Find(int x)
{
if(!fa[x]||fa[x]==x)
return fa[x]=x;
return fa[x]=Find(fa[x]);
}
void Union(int x,int y)
{
x=Find(x);y=Find(y);
if(x==y) return ;
fa[x]=y;
}
}s1,s2;
namespace Link_Cut_Tree{
struct abcd{
abcd *ls,*rs,*fa;
int val,sum;
bool rev_mark;
abcd() {}
abcd(int x);
void Push_Up();
void Push_Down();
void Reverse();
}tree[M],*null=&tree[0];
abcd :: abcd(int x)
{
ls=rs=fa=null;
val=sum=x;
rev_mark=false;
}
void abcd :: Push_Up()
{
sum=ls->sum+rs->sum+val;
}
void abcd :: Push_Down()
{
if(fa->ls==this||fa->rs==this)
fa->Push_Down();
if(rev_mark)
{
ls->Reverse();
rs->Reverse();
rev_mark=false;
}
}
void abcd :: Reverse()
{
swap(ls,rs);
rev_mark^=1;
}
void Zig(abcd *x)
{
abcd *y=x->fa;
y->ls=x->rs;
x->rs->fa=y;
x->rs=y;
x->fa=y->fa;
if(y==y->fa->ls)
y->fa->ls=x;
else if(y==y->fa->rs)
y->fa->rs=x;
y->fa=x;
y->Push_Up();
}
void Zag(abcd *x)
{
abcd *y=x->fa;
y->rs=x->ls;
x->ls->fa=y;
x->ls=y;
x->fa=y->fa;
if(y==y->fa->ls)
y->fa->ls=x;
else if(y==y->fa->rs)
y->fa->rs=x;
y->fa=x;
y->Push_Up();
}
void Splay(abcd *x)
{
x->Push_Down();
while(x->fa->ls==x||x->fa->rs==x)
{
abcd *y=x->fa,*z=y->fa;
if(x==y->ls)
{
if(y==z->ls)
Zig(y);
Zig(x);
}
else
{
if(y==z->rs)
Zag(y);
Zag(x);
}
}
x->Push_Up();
}
void Access(abcd *x)
{
abcd *y=null;
while(x!=null)
{
Splay(x);
x->rs=y;
x->Push_Up();
x->fa=&tree[s2.Find(x->fa-tree)];
y=x;x=x->fa;
}
}
void Move_To_Root(abcd *x)
{
Access(x);
Splay(x);
x->Reverse();
}
void Link(abcd *x,abcd *y)
{
Move_To_Root(x);
x->fa=y;
}
void Reduction(abcd *&x,abcd *y)
{
if(x==null)
return ;
s2.Union(x-tree,y-tree);
y->val+=x->val;
Reduction(x->ls,y);
Reduction(x->rs,y);
x=null;
}
}
namespace IStream{
#define L (1<<16)
char Get_Char()
{
static char buffer[M],*S,*T;
if(S==T)
{
T=(S=buffer)+fread(buffer,1,L,stdin);
if(S==T) return EOF;
}
return *S++;
}
int Get_Int()
{
int re=0;
char c=Get_Char();
while(c<'0'||c>'9')
c=Get_Char();
while(c>='0'&&c<='9')
re=(re<<3)+(re<<1)+(c-'0'),c=Get_Char();
return re;
}
}
int main()
{
using namespace Link_Cut_Tree;
using namespace IStream;
int i,p,x,y;
cin>>n>>m;
for(i=1;i<=n;i++)
{
a[i]=Get_Int();
tree[i]=abcd(a[i]);
}
for(i=1;i<=m;i++)
{
p=Get_Int();
x=Get_Int();
y=Get_Int();
if(p==1)
{
x=s2.Find(x);y=s2.Find(y);
if(x==y) continue ;
if(s1.Find(x)!=s1.Find(y))
Link(&tree[x],&tree[y]),s1.Union(x,y);
else
{
Move_To_Root(&tree[x]);
Access(&tree[y]);
Splay(&tree[y]);
Reduction(tree[y].ls,&tree[y]);
Reduction(tree[y].rs,&tree[y]);
tree[y].Push_Up();
}
}
else if(p==2)
{
Splay(&tree[s2.Find(x)]);
tree[s2.Find(x)].val+=y-a[x];
tree[s2.Find(x)].Push_Up();
a[x]=y;
}
else
{
x=s2.Find(x);y=s2.Find(y);
if(s1.Find(x)!=s1.Find(y))
puts("-1");
else
{
Move_To_Root(&tree[x]);
Access(&tree[y]);
Splay(&tree[y]);
printf("%d\n",tree[y].sum);
}
}
}
return 0;
}