题目描述
题解
用lct维护一颗动态树。
如果连了某一条边形成了一个环,证明一次长跑这个环上的所有的点都可以被统计,所以可以将这个环缩成一个点。用ufs来实现。
那么一次长跑实际上就是在一条树链上跑,只有一个方向,在lct上维护一个sum就可以了。
时间复杂度是均摊的,因为每一个点至多被缩点一次,所以
O(k(mlogn+n)α(n))
。lct常数非常大。
在卡常数的时候,由于每一次splay之前是要将所有的店pushdown的,所以只需要在pushdown的过程中find就可以了,这样find少一点常数会小一点。
代码
#include<iostream>
#include<cstring>
#include<cstdio>
using namespace std;
#define N 300005
#define inf 2100000000
int n,m,opt,a,b;
int f[N],ch[N][2],w[N],key[N],sum[N],rev[N],stack[N],q[N];
int dig[20];
int read()
{
int x=0,f=1;char ch=getchar();
while (ch<'0'||ch>'9')
{
if (ch=='-') f=-1;
ch=getchar();
}
while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
return x*f;
}
namespace check
{
int f[N];
void clear()
{
for (int i=1;i<=n;++i) f[i]=i;
}
int find(int x)
{
if (f[x]==x) return x;
f[x]=find(f[x]);
return f[x];
}
void merge(int x,int y)
{
int f1=find(x),f2=find(y);
f[f1]=f2;
}
}
namespace UFS
{
int f[N];
void clear()
{
for (int i=1;i<=n;++i)
f[i]=i;
}
int find(int x)
{
if (x==f[x]) return x;
f[x]=find(f[x]);
return f[x];
}
void merge(int x,int y)
{
int f1=find(x),f2=find(y);
f[f1]=f2;
}
}
inline int get(int x)
{
return ch[f[x]][1]==x;
}
inline bool isroot(int x)
{
return ch[f[x]][0]!=x&&ch[f[x]][1]!=x;
}
inline void update(int x)
{
sum[x]=key[x];
if (ch[x][0]) sum[x]+=sum[ch[x][0]];
if (ch[x][1]) sum[x]+=sum[ch[x][1]];
}
inline void pushdown(int x)
{
if (x)
{
if (rev[x])
{
if (ch[x][0]) rev[ch[x][0]]^=1;
if (ch[x][1]) rev[ch[x][1]]^=1;
swap(ch[x][0],ch[x][1]);
rev[x]=0;
}
}
}
inline void rotate(int x)
{
int old=f[x],oldf=f[old],wh=get(x);
if (oldf&&!isroot(old)) ch[oldf][ch[oldf][1]==old]=x;
f[x]=oldf;
ch[old][wh]=ch[x][wh^1];
if (ch[old][wh]) f[ch[old][wh]]=old;
ch[x][wh^1]=old;
f[old]=x;
update(old);
update(x);
}
inline void splay(int x)
{
x=UFS::find(x);f[x]=UFS::find(f[x]);
int top=0;stack[++top]=x;
for (int i=x;!isroot(i);i=f[i])
{
f[i]=UFS::find(f[i]);
stack[++top]=f[i];
f[f[i]]=UFS::find(f[f[i]]);
}
for (int i=top;i;--i) pushdown(stack[i]);
for (int fa;!isroot(x);rotate(x))
if (!isroot(fa=f[x]))
rotate( (get(fa)==get(x))?fa:x );
}
inline void access(int x)
{
int t=0;
for (;x;t=x,x=f[x])
{
x=UFS::find(x);
splay(x);
ch[x][1]=t;
update(x);
}
}
inline void reverse(int x)
{
access(x);
splay(x);
rev[x]^=1;
}
inline int find(int x)
{
access(x);
splay(x);
while (ch[x][0]) x=ch[x][0];
return x;
}
inline void un(int x,int y)
{
reverse(y);
access(x);
splay(x);
int cnt=sum[x];
int l=0,r=0;
q[++r]=x;
while (l<r)
{
x=q[++l];
if (ch[x][0]) q[++r]=ch[x][0];
if (ch[x][1]) q[++r]=ch[x][1];
UFS::merge(x,y);
key[x]=sum[x]=f[x]=ch[x][0]=ch[x][1]=0;
}
key[y]=sum[y]=cnt;
f[y]=0;
}
inline void link(int x,int y)
{
reverse(x);
f[x]=y;
}
inline void change(int x,int y,int v)
{
access(x);
splay(x);
sum[x]+=v-w[y];key[x]+=v-w[y];
update(x);
}
inline int query(int x,int y)
{
reverse(x);
access(y);
splay(y);
return sum[y];
}
inline void write(int x)
{
if (!x)
{
putchar('0');
putchar('\n');
return;
}
dig[0]=0;
while (x)
{
dig[++dig[0]]=x%10;
x/=10;
}
for (int i=dig[0];i>=1;--i) putchar(dig[i]+'0');
putchar('\n');
}
int main()
{
n=read();m=read();
for (int i=1;i<=n;++i) w[i]=key[i]=sum[i]=read();
UFS::clear();
check::clear();
while (m--)
{
opt=read();a=read();b=read();
switch(opt)
{
case 1:
{
a=UFS::find(a);b=UFS::find(b);
if (a==b) break;
int f1=check::find(a),f2=check::find(b);
if (f1==f2) un(a,b);
else link(a,b),check::f[f1]=f2;
break;
}
case 2:
{
int c=UFS::find(a);
if (w[a]!=b) change(c,a,b);
w[a]=b;
break;
}
case 3:
{
a=UFS::find(a);b=UFS::find(b);
if (check::find(a)!=check::find(b)) puts("-1");
else
{
int ans=query(a,b);
write(ans);
}
break;
}
}
}
}