题意:
给出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; }