bzoj2959: 长跑
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <algorithm> 6 #define maxn 150005 7 using namespace std; 8 9 int n,m,val[maxn],tot[maxn],fa[maxn],sum[maxn],son[maxn][2],bel[maxn],bel_[maxn]; 10 bool rev[maxn]; 11 12 struct date{ 13 int isroot(int x){ 14 return son[fa[x]][0]!=x&&son[fa[x]][1]!=x; 15 } 16 bool which(int x){ 17 return son[fa[x]][1]==x; 18 } 19 void update(int x){ 20 sum[x]=tot[x]; 21 if (son[x][0]) sum[x]+=sum[son[x][0]]; 22 if (son[x][1]) sum[x]+=sum[son[x][1]]; 23 } 24 void pushdown(int x){ 25 if (!rev[x]) return; 26 swap(son[x][0],son[x][1]),rev[x]^=1; 27 if (son[x][0]) rev[son[x][0]]^=1; 28 if (son[x][1]) rev[son[x][1]]^=1; 29 } 30 void relax(int x){ 31 if (!isroot(x)) relax(fa[x]); 32 pushdown(x); 33 } 34 void rotata(int x){ 35 int y=fa[x],d=which(x),dd=which(y); 36 if (!isroot(y)) son[fa[y]][dd]=x; fa[x]=fa[y]; 37 fa[son[x][d^1]]=y,son[y][d]=son[x][d^1]; 38 fa[y]=x,son[x][d^1]=y; 39 update(y); 40 } 41 void splay(int x){ 42 relax(x); 43 while (!isroot(x)){ 44 if (isroot(fa[x])) rotata(x); 45 else if (which(x)==which(fa[x])) rotata(fa[x]),rotata(x); 46 else rotata(x),rotata(x); 47 } 48 update(x); 49 } 50 int find(int x){ 51 if (bel[x]!=x) bel[x]=find(bel[x]); 52 return bel[x]; 53 } 54 int find_(int x){ 55 if (bel_[x]!=x) bel_[x]=find_(bel_[x]); 56 return bel_[x]; 57 } 58 void access(int x){ 59 for (int p=0;x;fa[x]=find(fa[x]),x=fa[x]){ 60 splay(x); 61 son[x][1]=p; 62 p=x; 63 update(x); 64 } 65 } 66 void make_root(int x){ 67 access(x); 68 splay(x); 69 rev[x]^=1; 70 } 71 void link(int x,int y){ 72 make_root(x); 73 fa[x]=y; 74 } 75 void merge(int x,int y){ 76 bel[x]=y; 77 if (x!=y) tot[y]+=tot[x]; 78 pushdown(x); 79 if (son[x][0]) merge(son[x][0],y); 80 if (son[x][1]) merge(son[x][1],y); 81 } 82 void build(int x,int y){ 83 if (x==y) return; 84 x=find(x),y=find(y); 85 int xx=find_(x),yy=find_(y); 86 if (xx!=yy) link(x,y),bel_[xx]=yy; 87 else{ 88 make_root(x),access(y),splay(y),merge(y,y); 89 } 90 } 91 void change(int x,int y){ 92 int t=find(x); 93 splay(t),tot[t]-=val[x],val[x]=y,tot[t]+=val[x],update(t); 94 } 95 void query(int x,int y){ 96 x=find(x),y=find(y); 97 if (find_(x)!=find_(y)) printf("-1\n"); 98 else make_root(x),access(y),splay(y),printf("%d\n",sum[y]); 99 } 100 }lct; 101 102 int main(){ 103 freopen("race.in","r",stdin); 104 freopen("race.out","w",stdout); 105 memset(rev,0,sizeof(rev)); 106 int op,u,v; 107 scanf("%d%d",&n,&m); 108 for (int i=1;i<=n;i++) scanf("%d",&u),val[i]=tot[i]=sum[i]=u,rev[i]=fa[i]=son[i][0]=son[i][1]=0,bel[i]=bel_[i]=i; 109 while (m--){ 110 scanf("%d%d%d",&op,&u,&v); 111 if (op==1) lct.build(u,v); 112 else if (op==2) lct.change(u,v); 113 else lct.query(u,v); 114 } 115 return 0; 116 }
题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=2959
题目大意:给定无向图,有n个节点,初始时节点之间没有边,节点的初始节点权值为val[i],有m个操作:
1.在a,b之间连一条无向边;
2.将节点a的权值改为b;
3.询问操作:给定a,b,要你给图中无向边定向,再寻找一条路径,满足路径上的节点权值之和最大,不一定是简单路径,即每个点可以经过多次,但权值只会算一次,若不存在这样的路径,则输出-1。
做法:如果没有操作1,我们可以把原图中的边双连通分量缩点,该点的权值为该边双连通分量中节点的权值之和,缩完点后,会变成一个森林,对于操作2与操作3,用树链剖分即可。
有了操作1,我们会想到用lct来维护,加入一条边后,如果不形成环,就加入,否则,将链上的点缩成一个点,用并查集维护即可。
注意:access的时候这能把双联通分量的代表点加入splay中,否则答案会重复算,具体见access过程。
由于这题卡常数,没有删边操作,在判断连通性时不能用find_root(x),应再用一个并查集来维护。
lct+并查集