bzoj 2959 长跑(LCT+BCC+并查集)

【题目链接】

http://www.lydsy.com/JudgeOnline/problem.php?id=2959

【题意】

n个点,提供操作:连边,修改点权,查询自定义边的方向后起点a终点b能经过的最大点权和。

【思路】

对于一个边的双连通分量,显然可以将权值全部获得。

如果没有连边操作,我们只需要将一个bcc缩点后求得a->b路径上的点权和即可。

加上连边后,使用并查集代表一个bcc,如果u,v之间不连通直接连边,如果已经连通则构成一个bcc,使用并查集将LCT的所有节点合并。

注意缩点后以并查集的代表元为该点,Access上找父亲的时候应该用父亲的bcc代表元。

【代码】

 #include<set>
#include<cmath>
#include<queue>
#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define FOR(a,b,c) for(int a=(b);a<=(c);a++)
using namespace std; typedef long long ll;
const int N = 3e5+; ll read() {
char c=getchar();
ll f=,x=;
while(!isdigit(c)) {
if(c=='-') f=-; c=getchar();
}
while(isdigit(c))
x=x*+c-'',c=getchar();
return x*f;
} struct UFS {
int p[N];
int ifind(int x) {
if(p[x]==||x==p[x]) return p[x]=x;
return p[x]=ifind(p[x]);
}
void iunion(int x,int y) {
x=ifind(x),y=ifind(y);
if(x!=y) p[x]=y;
}
} bcc,S; namespace LCT { struct Node {
Node *ch[],*fa;
int v,rev,sum;
Node() {}
Node(int x) ;
void reverse() {
rev^=;
swap(ch[],ch[]);
}
void up_push() {
if(fa->ch[]==this||fa->ch[]==this)
fa->up_push();
if(rev) {
ch[]->reverse();
ch[]->reverse();
rev=;
}
}
void maintain() {
sum=v+ch[]->sum+ch[]->sum;
}
} T[N],*null=&T[];
Node::Node(int x) {
rev=;
v=sum=x;
ch[]=ch[]=fa=null;
} void rot(Node* o,int d) {
Node *p=o->fa;
p->ch[d]=o->ch[d^];
o->ch[d^]->fa=p;
o->ch[d^]=p;
o->fa=p->fa;
if(p==p->fa->ch[])
p->fa->ch[]=o;
else if(p==p->fa->ch[])
p->fa->ch[]=o;
p->fa=o;
p->maintain();
}
void splay(Node *o) {
o->up_push();
Node *nf,*nff;
while(o->fa->ch[]==o||o->fa->ch[]==o) {
nf=o->fa,nff=nf->fa;
if(o==nf->ch[]) {
if(nf==nff->ch[]) rot(nf,);
rot(o,);
} else {
if(nf==nff->ch[]) rot(nf,);
rot(o,);
}
}
o->maintain();
}
void Access(Node *o) {
Node *son=null;
while(o!=null) {
splay(o);
o->ch[]=son;
o->maintain();
o->fa=&T[bcc.ifind(o->fa-T)];
son=o; o=o->fa;
}
}
void evert(Node *o) {
Access(o);
splay(o);
o->reverse();
}
void Link(Node *u, Node *v) {
evert(u);
u->fa=v;
} } using namespace LCT; int n,m,a[N]; void merge(Node* &u,Node* v)
{
if(u==null) return ;
v->v+=u->v;
bcc.iunion(u-T,v-T);
merge(u->ch[],v);
merge(u->ch[],v);
u=null;
} int main()
{
n=read(),m=read();
FOR(i,,n) {
a[i]=read(); T[i]=Node(a[i]);
}
int op,u,v;
FOR(i,,m) {
op=read(),u=read(),v=read();
if(op==) {
u=bcc.ifind(u),v=bcc.ifind(v);
if(u==v) continue;
if(S.ifind(u)!=S.ifind(v))
Link(&T[u],&T[v]),
S.iunion(u,v);
else {
evert(&T[u]);
Access(&T[v]),splay(&T[v]);
merge(T[v].ch[],&T[v]);
merge(T[v].ch[],&T[v]);
T[v].maintain();
}
} else
if(op==) {
splay(&T[bcc.ifind(u)]);
T[bcc.ifind(u)].v+=v-a[u];
T[bcc.ifind(u)].maintain();
a[u]=v;
} else {
u=bcc.ifind(u),v=bcc.ifind(v);
if(S.ifind(u)!=S.ifind(v)) puts("-1");
else {
evert(&T[u]);
Access(&T[v]),splay(&T[v]);
printf("%d\n",T[v].sum);
}
}
}
return ;
}

Q:打码的最怕什么

A:手残和眼瞎。

真不巧,我两样都是

上一篇:mysql查询两张表更改一张表数据


下一篇:HDU 3549 Flow Problem(最大流模板)