[NOI2014]魔法森林

                ★★★☆   输入文件:magicalforest.in   输出文件:magicalforest.out   简单对比
                      时间限制:2 s   内存限制:512 MB

题解:

  每条边有两种权值,所以想到先对一种权值A进行排序,然后一条一条地加边,如果有环,就特殊判断,在这题中就是删掉该环上B值最大的边。直到1和N之间连通,就更新答案。

  由于牵涉到连边和删边的操作,所以可以用动态树来维护。但是动态树里都是点权,题目中的边权很难处理,所以我们可以在两点之间的边上多加一个点,中间的点的权值就是边权,两端点的权值就是0,三个点成一条边,为了方便起见,中间的点的编号设为N+i,i为当前边的编号,以此来增减边。

  还有一个问题,就是如何找到某环上的最大B值。对于当前要连接的两点(u,v),如果连了,就会形成环,现在rever(u),再access(v),u和v之间的路径就存在一棵splay里了,可以给splay定义一个mx[]值,mx[i]=k的意思是在以i为根的splay里,点权最大的点是k点,再搞一个val[]数组记录下点的B值。每次splay的时候更新一下,还有在cut,link的时候也要更新。

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstdlib>
  4 #include<cmath>
  5 #include<cstring>
  6 #include<algorithm>
  7 #include<vector>
  8 #include<queue>
  9 using namespace std;
 10 const int maxn=50010*4;
 11 inline int read(){
 12     int x=0,f=1;char ch=getchar();
 13     while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
 14     while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
 15     return x*f;
 16 }
 17 int N,M,ANS=1e9;
 18 struct node{
 19     int x,y,A,B;
 20 }e[maxn];
 21 int cmp(const node & a,const node & b){
 22     if(a.A<b.A) return 1;
 23     return 0;
 24 }
 25 int c[maxn][2],fa[maxn],st[maxn];
 26 int mx[maxn],val[maxn];
 27 bool rev[maxn];
 28 bool isroot(int x){
 29     return x!=c[fa[x]][0]&&x!=c[fa[x]][1];
 30 }
 31 void pushup(int x){
 32     int l=c[x][0],r=c[x][1];
 33     mx[x]=x; 
 34     if(val[mx[l]]>val[mx[x]]) mx[x]=mx[l];
 35     if(val[mx[r]]>val[mx[x]]) mx[x]=mx[r];
 36 }
 37 void pushdown(int x){
 38     int l=c[x][0],r=c[x][1];
 39     if(rev[x]==true){
 40         rev[x]^=1; rev[l]^=1; rev[r]^=1;
 41         swap(c[x][0],c[x][1]);
 42     }
 43 }
 44 void rotate(int x){
 45     int y=fa[x],z=fa[y],l,r;
 46     if(x==c[y][0]) l=0;else l=1;r=l^1;
 47     if(!isroot(y)){
 48         if(y==c[z][0]) c[z][0]=x;
 49         else c[z][1]=x;
 50     }
 51     fa[x]=z; fa[y]=x; fa[c[x][r]]=y;
 52     c[y][l]=c[x][r]; c[x][r]=y;
 53     pushup(y); pushup(x);
 54 }
 55 
 56 void splay(int x){
 57     int top=0; st[++top]=x;
 58     for(int i=x;!isroot(i);i=fa[i]){
 59         st[++top]=fa[i];
 60     }
 61     for(int i=top;i;i--) pushdown(st[i]); 
 62     while(!isroot(x)){
 63         int y=fa[x],z=fa[y];
 64         if(!isroot(y)){
 65             if((x==c[y][0]&&y==c[z][0])||(x==c[y][1]&&y==c[z][1])){
 66                 rotate(y); rotate(x);
 67             }
 68             else rotate(x),rotate(x);
 69         }
 70         else rotate(x);
 71     }
 72 }
 73 void access(int x){
 74     int t=0;
 75     while(x){
 76         splay(x);
 77         c[x][1]=t;
 78         pushup(x);
 79         t=x; x=fa[x];        
 80     }
 81 }
 82 void rever(int x){
 83     access(x); splay(x); rev[x]^=1;
 84 }
 85 void cut(int x,int y){
 86     rever(x); access(y); splay(y);
 87     c[y][0]=fa[x]=0; pushup(y);
 88 }
 89 void link(int x,int y){
 90     rever(x); fa[x]=y; splay(x); 
 91 }
 92 int find(int x){
 93     access(x); splay(x);
 94     while(c[x][0]) x=c[x][0];
 95     return x;
 96 }
 97 int query(int x,int y){
 98     rever(x); access(y); splay(y);
 99     return mx[y];
100 }
101 int main(){
102     freopen("magicalforest.in","r",stdin);
103     freopen("magicalforest.out","w",stdout); 
104     N=read(); M=read();
105     for(int i=1;i<=M;i++){
106         e[i].x=read(); e[i].y=read();
107         e[i].A=read(); e[i].B=read();
108     }
109     sort(e+1,e+M+1,cmp);
110     int tot=0;
111     for(int i=1;i<=M;i++){
112         int u=e[i].x,v=e[i].y,a=e[i].A,b=e[i].B;
113         if(find(u)==find(v)){//在同一个连通块中 
114             int t=query(u,v);//删去一个环里B值最大的边 
115             if(val[t]>e[i].B){
116                 cut(t,e[t-N].x);
117                 cut(t,e[t-N].y);
118             }
119             else{
120                 if(find(1)==find(N))
121                     ANS=min(ANS,e[i].A+val[query(1,N)]);
122                 continue;
123             }
124         }
125         val[N+i]=e[i].B; mx[N+i]=N+i;//u,v之间添加一个节点,点权即为边权 
126         link(u,N+i); link(v,N+i);
127         if(find(1)==find(N)) ANS=min(ANS,e[i].A+val[query(1,N)]);//更新 
128     }
129     if(ANS==1e9) puts("-1");
130     else printf("%d\n",ANS);
131     return 0;
132 }

 

转载于:https://www.cnblogs.com/CXCXCXC/p/5191657.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值