zoukankan      html  css  js  c++  java
  • BZOJ 3669 【NOI2014】 魔法森林

    Description

    为了得到书法大家的真传,小E同学下定决心去拜访住在魔法森林中的隐士。魔法森林可以被看成一个包含个N节点M条边的无向图,节点标号为1..N,边标号为1..M。初始时小E同学在号节点1,隐士则住在号节点N。小E需要通过这一片魔法森林,才能够拜访到隐士。

    魔法森林中居住了一些妖怪。每当有人经过一条边的时候,这条边上的妖怪就会对其发起攻击。幸运的是,在号节点住着两种守护精灵:A型守护精灵与B型守护精灵。小E可以借助它们的力量,达到自己的目的。

    只要小E带上足够多的守护精灵,妖怪们就不会发起攻击了。具体来说,无向图中的每一条边Ei包含两个权值Ai与Bi。若身上携带的A型守护精灵个数不少于Ai,且B型守护精灵个数不少于Bi,这条边上的妖怪就不会对通过这条边的人发起攻击。当且仅当通过这片魔法森林的过程中没有任意一条边的妖怪向小E发起攻击,他才能成功找到隐士。

    由于携带守护精灵是一件非常麻烦的事,小E想要知道,要能够成功拜访到隐士,最少需要携带守护精灵的总个数。守护精灵的总个数为A型守护精灵的个数与B型守护精灵的个数之和。

    Input

    第1行包含两个整数N,M,表示无向图共有N个节点,M条边。 接下来M行,第行包含4个正整数Xi,Yi,Ai,Bi,描述第i条无向边。其中Xi与Yi为该边两个端点的标号,Ai与Bi的含义如题所述。 注意数据中可能包含重边与自环。

    Output

    输出一行一个整数:如果小E可以成功拜访到隐士,输出小E最少需要携带的守护精灵的总个数;如果无论如何小E都无法拜访到隐士,输出“-1”(不含引号)。

    Sample Input

    【输入样例1】
    4 5
    1 2 19 1
    2 3 8 12
    2 4 12 15
    1 3 17 8
    3 4 1 17
    【输入样例2】
    3 1
    1 2 1 1

    Sample Output

    【输出样例1】

    32
    【样例说明1】
    如果小E走路径1→2→4,需要携带19+15=34个守护精灵;
    如果小E走路径1→3→4,需要携带17+17=34个守护精灵;
    如果小E走路径1→2→3→4,需要携带19+17=36个守护精灵;
    如果小E走路径1→3→2→4,需要携带17+15=32个守护精灵。
    综上所述,小E最少需要携带32个守护精灵。
    【输出样例2】
    -1
    【样例说明2】
    小E无法从1号节点到达3号节点,故输出-1。

    HINT

    2<=n<=50,000

    0<=m<=100,000

    1<=ai ,bi<=50,000

      话说这道题被神犇xzy推荐给我写spfa入门题,然后狂TLE不止...把这道题当成入门题给我我也是醉了......

      这一题其实我们只需将边按a权值排序,再一条边一条边地插入,每次跑一遍最短路即可轻松AC。(o_o)

      好吧,其实spfa还是我很久以前写的了。显然裸的spfa肯定是跑不过的,于是我们需要加一点小小的优化,那就是每次不清空dis数组,改为直接将新边连接的两个点加入队列,跑一遍spfa(话说我也不知道这为什么复杂度是对的【其实是不对的】)就可以AC了。

      UPD:已经在UOJ上被卡掉了

      下面贴代码:

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstdlib>
     4 #include<cstring>
     5 #include<algorithm>
     6 #include<cmath>
     7 #define maxn 100001
     8 #define INF 2147483647
     9 
    10 using namespace std;
    11 typedef long long llg;
    12 
    13 struct data{
    14     int t,f,c1,c2;
    15     bool operator < (const data &h)const{return c1<h.c1;}
    16 }s[maxn];
    17 int head[maxn],to[maxn<<1],next[maxn<<1],l,r;
    18 int c[maxn<<1],dis[maxn],d[maxn],n,m,tt,ans;
    19 bool w[maxn];
    20 
    21 int getint(){
    22     int w=0,q=0;
    23     char c=getchar();
    24     while((c<'0'||c>'9')&&c!='-') c=getchar();
    25     if(c=='-') q=1,c=getchar();
    26     while(c>='0'&&c<='9') w=w*10+c-'0',c=getchar();
    27     return q?-w:w;
    28 }
    29 
    30 void spfa(){
    31     d[r++]=1;w[1]=1;
    32     while(l!=r){
    33         int u=d[l++];l%=maxn;w[u]=0;
    34         for(int i=head[u],v;v=to[i],i;i=next[i])
    35             if(dis[v]==-1 || dis[v]>max(dis[u],c[i])){
    36                 dis[v]=max(dis[u],c[i]);
    37                 if(!w[v]){
    38                     w[v]=1;d[r++]=v;
    39                     r%=maxn;
    40                 }
    41             }
    42     }
    43 }
    44 
    45 int main(){
    46     n=getint();m=getint();
    47     memset(dis,-1,sizeof(dis));
    48     for(int i=1;i<=m;i++){
    49         s[i].t=getint();s[i].f=getint();
    50         s[i].c1=getint();s[i].c2=getint();
    51     }
    52     sort(s+1,s+m+1);ans=INF;dis[1]=0;
    53     for(int i=1;i<=m;i++){
    54         int f=s[i].f,t=s[i].t;
    55         to[++tt]=f;next[tt]=head[t];head[t]=tt;
    56         to[++tt]=t;next[tt]=head[f];head[f]=tt;
    57         c[tt-1]=c[tt]=s[i].c2;l=r=0;
    58         if(dis[t]!=-1) d[r++]=t,w[t]=1;
    59         if(dis[f]!=-1) d[r++]=f,w[f]=1;
    60         spfa();
    61         if(dis[n]!=-1) ans=min(ans,dis[n]+s[i].c1);
    62     }
    63     if(ans==INF) printf("-1");
    64     else printf("%d",ans);
    65 }
    View Code

      下面我还是准备讲讲复杂度很对的一种做法(虽然跑的更慢)。我们仍然将边按a权值排序,然后我们只要快速求出1到n的最小生成树(或者最短路也可以)。虽然spfa可以跑过,但这个算法复杂度毕竟不对,我们还是要追求一种复杂度比较对的算法。其实,我们只要动态维护最小生成树就可以了。那么用什么呢?CDQ图分治或许可以,但我选择了LCT。其实以前我对于边带权值的动态树我一直是懵逼的,直到orz了hzwer大神的题解,我才发现了一种极其优秀的做法。我们可以把边也建成节点,而且只有边所代表的节点才有权值。这样就非常好做了是不?我们splay上维护一个区间最大值不就可以了是么?剩下的就是link-cut-tree的模板了。

      我觉得这道题是一道LCT的好题。如果你还不会LCT,请前往:[Hnoi2010]Bounce 弹飞绵羊 [Sdoi2008]Cave 洞穴勘测

      如果你会LCT但是就是A不了这题,推荐你还是仔细啃一啃这道题的代码吧。

      这道题AC代码(LCT)如下:

      1 #include<iostream>
      2 #include<cstdio>
      3 #include<cstring>
      4 #include<algorithm>
      5 #include<cmath>
      6 #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
      7 #define maxn 150010
      8 #define maxm 100010
      9 
     10 using namespace std;
     11 typedef long long llg;
     12 
     13 struct data{
     14     int u,v,a,b;
     15     bool operator < (const data &h)const{return a<h.a;}
     16 }ss[maxm];
     17 int val[maxn],s[maxn][2],fa[maxn],maxv[maxn],d[maxn],ans,n,m;
     18 bool rev[maxn];
     19 
     20 int getint(){
     21     int w=0;bool q=0;
     22     char c=getchar();
     23     while((c>'9'||c<'0')&&c!='-') c=getchar();
     24     if(c=='-') q=1,c=getchar();
     25     while(c>='0'&&c<='9') w=w*10+c-'0',c=getchar();
     26     return q?-w:w;
     27 }
     28 
     29 bool isroot(int x){return x!=s[fa[x]][0] && x!=s[fa[x]][1];}
     30 int findrt(int x){while(fa[x]) x=fa[x];return x;}
     31 
     32 void update(int x){
     33     int l=s[x][0],r=s[x][1];maxv[x]=x;
     34     if(val[maxv[l]]>val[maxv[x]]) maxv[x]=maxv[l];
     35     if(val[maxv[r]]>val[maxv[x]]) maxv[x]=maxv[r];
     36 }
     37 
     38 void R(int x){
     39     int p=fa[x],g=fa[p];
     40     bool l=(x==s[fa[x]][0]),r=!l;
     41     if(!isroot(p)) s[g][p==s[g][1]]=x;
     42     fa[s[x][l]]=p; s[p][r]=s[x][l];
     43     fa[p]=x; s[x][l]=p; fa[x]=g;
     44     update(p); update(x);
     45 }
     46 
     47 void splay(int x){ //将x旋到当前splay的根节点
     48     d[d[0]=1]=x;
     49     for(int i=x;!isroot(i);i=fa[i]) d[++d[0]]=fa[i];
     50     for(int i=d[0],u;u=d[i],i;i--)
     51         if(rev[u]){
     52             swap(s[u][0],s[u][1]);rev[u]=0;
     53             rev[s[u][0]]^=1;rev[s[u][1]]^=1;
     54         }
     55     while(!isroot(x)){
     56         int p=fa[x],g=fa[p];
     57         if(!isroot(p)){
     58             if((x==s[p][0])^(p==s[g][0])) R(x);
     59             else R(p);
     60         }
     61         R(x);
     62     }
     63 }
     64 
     65 void access(int u){ //将u到splay根节点的路径打通
     66     for(int t=0;u;t=u,u=fa[u])
     67         splay(u),s[u][1]=t,update(u);
     68 }
     69 
     70 void makert(int u){ //把u换到它所在splay的根,并把沿途路径反向
     71     access(u);
     72     splay(u);rev[u]^=1;
     73 }
     74 
     75 int query(int u,int v){ //查询u到v的边中的最大值
     76     makert(u);access(v);splay(v);
     77     return maxv[v];
     78 }
     79 
     80 void cut(int u,int v){ //把u和v的连接断掉,谁是父亲无关紧要
     81     makert(u);access(v);splay(v);
     82     s[v][0]=fa[u]=0; update(v);
     83 }
     84 
     85 void link(int u,int v){ //把u的父亲变成v
     86     makert(u);fa[u]=v;
     87 }
     88 
     89 int main(){
     90     File("a");
     91     n=getint();m=getint();
     92     for(int i=1;i<=m;i++){
     93         ss[i].u=getint();ss[i].v=getint();
     94         ss[i].a=getint();ss[i].b=getint();
     95     }
     96     sort(ss+1,ss+m+1);ans=(1<<30);
     97     for(int i=1,u,v,t;i<=m;i++){
     98         u=ss[i].u;v=ss[i].v;
     99         if(findrt(u)==findrt(v)){
    100             t=query(u,v);
    101             if(val[t]>ss[i].b){
    102                 cut(ss[t-n].u,t);
    103                 cut(ss[t-n].v,t);
    104             }
    105             else continue;
    106         }
    107         val[i+n]=ss[i].b;maxv[i+n]=i+n;
    108         link(u,i+n);link(v,i+n);
    109         if(findrt(1)==findrt(n))
    110             ans=min(ans,ss[i].a+val[query(1,n)]);
    111     }
    112     printf(ans==(1<<30)?"-1":"%d",ans);
    113     return 0;
    114 }
    View Code
  • 相关阅读:
    SSH和SSL比较
    SSL虚拟主机安全方案
    https在电子邮件安全解决方案
    centos tomcat安装
    laravel 添加第三方扩展库
    laravel-1 安装.配置
    centos7.0 vsftp配置
    centos 日常操作指令
    centos redis 安装
    centos php 扩展安装
  • 原文地址:https://www.cnblogs.com/lcf-2000/p/5571214.html
Copyright © 2011-2022 走看看