BZOJ 2959 长跑
题目链接:长跑
这道题要求维护一张图,有三个操作:
1.在\(u\),\(v\)之间加入一条边;
2.把\(u\)点的权值修改为\(v\);
3.要求把所有边定向,询问从\(u\)到\(v\)的路径可以经过的最大点权和。路径可以是非简单路径,同一个点权值只算一次。
首先可以发现如果形成了一个边双,那么这个边双内的所有点的贡献就都可以一起算上了。所以每次加边的时候,如果形成了一个边双,那么就暴力把他们给合并了,每次就只需要询问路径点权和了。这个用\(LCT\)维护即可。缩点的时候可以用并查集维护一下每个点缩点后所属的点。复杂度\(O(m\alpha(n)\log n)\)
还有一种树链剖分的做法。我们可以先离线构出树,那么同理,如果某次操作后出现了边双,我们就可以直接把这些点的权值集中到\(lca\),其余位置清\(0\)。用\(LCT\)实现这个也是可以的,但是用树链剖分套树状数组会更快。复杂度\(O(m\log n)\)或者\(O(m\log^2 n)\)
下面贴代码(第一种做法):
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
#define isroot(x) (x!=s[fa[x]][0] && x!=s[fa[x]][1])
#define maxn 150010
using namespace std;
typedef long long llg;
int n,m,val[maxn],sumv[maxn],a[maxn];
int fa[maxn],s[maxn][2],d[maxn];
int f1[maxn],f2[maxn],s1[maxn],s2[maxn];
bool rev[maxn];
int getint(){
int w=0;bool q=0;
char c=getchar();
while((c>'9'||c<'0')&&c!='-') c=getchar();
if(c=='-') c=getchar(),q=1;
while(c>='0'&&c<='9') w=w*10+c-'0',c=getchar();
return q?-w:w;
}
int fi1(int x){return f1[f1[x]]==f1[x]?f1[x]:f1[x]=fi1(f1[x]);}
int fi2(int x){return f2[f2[x]]==f2[x]?f2[x]:f2[x]=fi2(f2[x]);}
void update(int u){sumv[u]=sumv[s[u][0]]+sumv[s[u][1]]+val[u];}
void R(int u){
int p=fa[u],g=fa[p];
bool l=(u==s[p][1]),r=!l;
if(!isroot(p)) s[g][p==s[g][1]]=u;
fa[s[u][r]]=p; s[p][l]=s[u][r];
s[u][r]=p; fa[p]=u; fa[u]=g;
update(p); update(u);
}
void splay(int u){
int ld=0; d[ld=1]=u;
for(int i=u;!isroot(i);i=fa[i]) d[++ld]=fa[i];
for(int i=ld,x;x=d[i],i;i--)
if(rev[x]){
rev[s[x][0]]^=1,rev[s[x][1]]^=1;
swap(s[x][0],s[x][1]); rev[x]=0;
}
while(!isroot(u)){
int p=fa[u],g=fa[p];
if(!isroot(p)){
if((u==s[p][1])^(p==s[g][1])) R(u);
else R(p);
}
R(u);
}
}
void access(int u){
for(int t=0;u;t=u,u=fa[u]=fi1(fa[u]))
splay(u),s[u][1]=t,update(u);
}
void makert(int u){access(u); splay(u); rev[u]^=1;}
void merge(int x,int y){
x=fi1(x),y=fi1(y);
if(s1[x]>s1[y]) x^=y^=x^=y;
f1[x]=y; s1[y]+=s1[x];
}
void dfs(int u,int fa){
if(u!=fa) merge(u,fa);
if(s[u][0]) dfs(s[u][0],u);
if(s[u][1]) dfs(s[u][1],u);
}
int main(){
File("a");
n=getint(),m=getint();
for(int i=1;i<=n;i++){
a[i]=val[i]=sumv[i]=getint();
f1[i]=f2[i]=i,s1[i]=s2[i]=1;
}
while(m--){
int op=getint(),u=getint(),v=getint();
if(op==1){
if(fi2(u)!=fi2(v)){
makert(fi1(u)),fa[fi1(u)]=fi1(v);
u=fi2(u),v=fi2(v);
if(s2[u]>s2[v]) u^=v^=u^=v;
s2[v]+=s2[u]; f2[u]=v;
}
else if(fi1(u)!=fi1(v)){
makert(u=fi1(u)); v=fi1(v);
access(v),splay(v); dfs(v,v); splay(v=fi1(v));
val[v]=sumv[v]; s[v][0]=s[v][1]=0;
}
}
else if(op==2){
int x=fi1(u); makert(x); val[x]+=v-a[u];
sumv[x]+=v-a[u]; a[u]=v;
}
else if(op==3)
if(fi2(u)!=fi2(v)) printf("-1\n");
else{
u=fi1(u); v=fi1(v);
makert(u),access(v),splay(v);
printf("%d\n",sumv[v]);
}
}
return 0;
}

浙公网安备 33010602011771号