题意:
给出n个结点,每个点上有一个点权;
有三种操作,共m次;
1.连接两个点,如果已经在一个连通块中则忽略此操作;
2.修改某个点的点权;
3.查询两个点之间点权和;
n<=30000,m<=300000,点权<=1000;
假装强制在线;
题解:
很久以前写过这道题的LCT解法,因为这么做太傻逼就没写题解;
不过因为一些奇 怪的原因我又用一种奇 怪的方法写了一遍这题,所以来发篇题解咯;
【首先假设我们都不会LCT】
当我们看到链上求值的时候,应当是不知所措的,因为通常来说问题的解法都是链剖;
因为这道题有了加边的操作,链剖并不能保证时间复杂度;
注意到查询的问题十分简单只有查点权和的操作,所以也就可以用入栈出栈序来搞一搞;
因为这里的序列是变化的,所以可以用Splay来维护每个树的序列,每次将小树暴力重构序列再插入到大树中就可以了;
这样重构的复杂度是O(nlogn),插入是O(logn),查询之类的显然也是O(logn),那么时间复杂度也就有保证了;
然而因为一些不可避免的原因我们要求LCA。。这就导致了空间上升到O(nlogn)了。。
【经过实际测试这个算法比LCT慢了一倍,并且因为不熟悉所以不太好写】
所以我投LCT一票!
代码:
LCT:
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 31000
#define which(x) (ch[fa[x]][1]==x)
using namespace std;
int fa[N],ch[N][2],val[N],sum[N];
bool rt[N],cov[N];
char str[20];
void Pushup(int x)
{
sum[x]=sum[ch[x][0]]+sum[ch[x][1]]+val[x];
}
void reverse(int x)
{
swap(ch[x][0],ch[x][1]);
cov[x]^=1;
}
void Pushdown(int x)
{
if(cov[x])
{
reverse(ch[x][0]);
reverse(ch[x][1]);
cov[x]=0;
}
}
void down(int x)
{
if(!rt[x]) down(fa[x]);
Pushdown(x);
}
void Rotate(int x)
{
int f=fa[x];
bool k=which(x);
if(rt[f]) rt[f]^=rt[x]^=1;
else ch[fa[f]][which(f)]=x;
ch[f][k]=ch[x][!k];
ch[x][!k]=f;
fa[ch[f][k]]=f;
fa[x]=fa[f];
fa[f]=x;
Pushup(f);
Pushup(x);
}
void Splay(int x)
{
down(x);
while(!rt[x])
{
int f=fa[x];
if(rt[f])
{
Rotate(x);
return ;
}
if(which(x)^which(f))
Rotate(x);
else
Rotate(f);
Rotate(x);
}
}
void access(int x)
{
int y=0;
while(x)
{
Splay(x);
rt[ch[x][1]]=1,rt[y]=0;
ch[x][1]=y;
Pushup(x);
y=x,x=fa[x];
}
}
void Mtr(int x)
{
access(x);
Splay(x);
reverse(x);
}
void Link(int x,int y)
{
Mtr(x);
fa[x]=y;
}
bool judge(int x,int y)
{
Mtr(x);
access(y);
Splay(x);
while(!rt[y])
y=fa[y];
return y==x;
}
void update(int x,int v)
{
Splay(x);
val[x]=v;
Pushup(x);
}
int query(int x,int y)
{
Mtr(x);
access(y);
Splay(x);
return sum[x];
}
int main()
{
int n,m,i,j,k,x,y,v;
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d",&v);
val[i]=sum[i]=v;
rt[i]=1;
}
scanf("%d",&m);
for(i=1;i<=m;i++)
{
scanf("%s",str);
if(str[0]=='b')
{
scanf("%d%d",&x,&y);
if(judge(x,y))
puts("no");
else
{
Link(x,y);
puts("yes");
}
}
else if(str[0]=='p')
{
scanf("%d%d",&x,&v);
update(x,v);
}
else
{
scanf("%d%d",&x,&y);
if(judge(x,y))
printf("%d\n",query(x,y));
else
puts("impossible");
}
}
return 0;
}
入栈出栈序+启发式合并:
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 31000
#define which(x) (ch[fa[x]][1]==x)
using namespace std;
int next[N<<1],to[N<<1],head[N],ce;
int fa[N<<1],ch[N<<1][2],val[N<<1],sum[N<<1];
int A[N][20],deep[N];
char str[20];
namespace Set
{
int f[N],size[N];
int find(int x)
{
return f[x]==x?x:f[x]=find(f[x]);
}
}
void add(int x,int y)
{
to[++ce]=y;
next[ce]=head[x];
head[x]=ce;
}
void Pushup(int x)
{
if(!x) return ;
sum[x]=sum[ch[x][0]]+sum[ch[x][1]]+val[x];
}
void Rotate(int x)
{
int f=fa[x];
bool k=which(x);
ch[f][k]=ch[x][!k];
ch[x][!k]=f;
ch[fa[f]][which(f)]=x;
fa[ch[f][k]]=f;
fa[x]=fa[f];
fa[f]=x;
Pushup(f);
Pushup(x);
}
void Splay(int x,int g)
{
while(fa[x]!=g)
{
int f=fa[x];
if(fa[f]==g)
{
Rotate(x);
break;
}
if(which(x)^which(f))
Rotate(x);
else
Rotate(f);
Rotate(x);
}
}
void dfs(int x,int &rt)
{
deep[x]=deep[A[x][0]]+1;
memset(A[x]+1,0,sizeof(int)*19);
for(int i=1;A[x][i-1];i++)
A[x][i]=A[A[x][i-1]][i-1];
ch[rt][1]=x<<1;
fa[x<<1]=rt;
ch[x<<1][0]=ch[x<<1][1]=0;
rt=x<<1;
for(int i=head[x];i;i=next[i])
{
if(to[i]!=A[x][0])
{
A[to[i]][0]=x;
dfs(to[i],rt);
}
}
ch[rt][1]=x<<1|1;
fa[x<<1|1]=rt;
ch[x<<1|1][0]=ch[x<<1|1][1]=0;
rt=x<<1|1;
}
void Link(int x,int y)
{
using namespace Set;
int fx=find(x),fy=find(y);
if(size[fx]>size[fy])
swap(x,y),swap(fx,fy);
f[fx]=fy;
size[fy]+=size[fx];
A[x][0]=y;
int rt=0;
for(dfs(x,rt);fa[rt];rt=fa[rt])
Pushup(rt);
Splay(y<<1,0);
Splay(y<<1|1,y<<1);
ch[rt][0]=ch[y<<1|1][0];
fa[ch[rt][0]]=rt;
fa[rt]=y<<1|1;
ch[y<<1|1][0]=rt;
Pushup(rt);
Pushup(y<<1|1);
Pushup(y<<1);
add(x,y),add(y,x);
}
int LCA(int x,int y)
{
int k=14;
if(deep[x]<deep[y])
swap(x,y);
while(k>=0)
{
if(deep[A[x][k]]>=deep[y])
x=A[x][k];
k--;
}
if(x==y) return x;
k=14;
while(k>=0)
{
if(A[x][k]!=A[y][k])
x=A[x][k],y=A[y][k];
k--;
}
return A[x][0];
}
int calc(int x)
{
if(!x) return 0;
Splay(x<<1,0);
return sum[ch[x<<1][0]]+val[x<<1];
}
int query(int x,int y)
{
int v=LCA(x,y);
return calc(x)+calc(y)-calc(v)-calc(A[v][0]);
}
int main()
{
int n,m,i,x,y;
scanf("%d",&n);
for(i=1;i<=n;i++)
{
scanf("%d",&x);
deep[i]=1;
val[i<<1]=x,val[i<<1|1]=-x;
fa[i<<1|1]=i<<1;
ch[i<<1][1]=i<<1|1;
sum[i<<1|1]=-x;
sum[i<<1]=0;
Set::f[i]=i;
Set::f[i]=i;
Set::size[i]=1;
}
scanf("%d",&m);
for(i=1;i<=m;i++)
{
scanf("%s",str);
if(str[0]=='b')
{
scanf("%d%d",&x,&y);
if(Set::find(x)==Set::find(y))
puts("no");
else
puts("yes"),Link(x,y);
}
else if(str[0]=='p')
{
scanf("%d%d",&x,&y);
Splay(x<<1,0);
val[x<<1]=y;
Pushup(x<<1);
Splay(x<<1|1,0);
val[x<<1|1]=-y;
Pushup(x<<1|1);
}
else
{
scanf("%d%d",&x,&y);
if(Set::find(x)!=Set::find(y))
puts("impossible");
else
printf("%d\n",query(x,y));
}
}
return 0;
}