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 }
View Code

题目链接: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+并查集

posted @ 2016-05-19 06:58  oyzx~  阅读(195)  评论(0编辑  收藏  举报