BZOJ 2959 长跑

题目链接:长跑

  这道题要求维护一张图,有三个操作:

  1.在(u),(v)之间加入一条边;

  2.把(u)点的权值修改为(v);

  3.要求把所有边定向,询问从(u)到(v)的路径可以经过的最大点权和。路径可以是非简单路径,同一个点权值只算一次。

  首先可以发现如果形成了一个边双,那么这个边双内的所有点的贡献就都可以一起算上了。所以每次加边的时候,如果形成了一个边双,那么就暴力把他们给合并了,每次就只需要询问路径点权和了。这个用(LCT)维护即可。缩点的时候可以用并查集维护一下每个点缩点后所属的点。复杂度(O(malpha(n)log n))

  还有一种树链剖分的做法。我们可以先离线构出树,那么同理,如果某次操作后出现了边双,我们就可以直接把这些点的权值集中到(lca),其余位置清(0)。用(LCT)实现这个也是可以的,但是用树链剖分套树状数组会更快。复杂度(O(mlog n))或者(O(mlog^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
");
			else{
				u=fi1(u); v=fi1(v);
				makert(u),access(v),splay(v);
				printf("%d
",sumv[v]);
			}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/lcf-2000/p/6624179.html