zoukankan      html  css  js  c++  java
  • BZOJ2330 糖果[差分约束方案+spfa?/tarjan]

    以往对于差分约束理解不是太深,导致这题屡次被坑,在此记录一下细节的理解。


    差分约束实际上就是利用了spfa的一个特性:只要有$dis_y>dis_x+w_{x,y}$就松弛,直到所有边关系都满足$dis_yle dis_x+w_{x,y}$,而这一不等式恰好可以套在差分约束问题里。差分约束要求满足前面这个玩意,把每个变量的值看做$dis_i$,建边,跑spfa最短路,使得所有边都满足关系。这个之前就已经明白了。

    但是忽视了一点:注意到跑出来的最短路中$dis$即为$x$的一组合法取值,并且这组取值同时加上某个值$a$,也是满足不等式约束的。不过,这些解集构成的集合,并不是解集集合的全集(有点绕),显然在跑spfa时只是使得每个点的$dis$贴着松弛他的$dis+w$,其实这个实际取值$x$还可以更小。而如果用最长路的建边方式,$x$可以更大。两者都是等效的。在这题里面,由于要求每个数都是正整数且总和最小,并不可以使用建负权边跑最短路的方法。。。因为解出来的解集即使全部平移到正数区域,你也不知道有没有哪些数可以更小一些,哪些数不能再小了。而建正权边跑最长路,根据上述分析,每个$x$也就是$dis$必然是贴着最小的可能取值的,这样就极其简便的获取最小解。

    第二个点,因为spfa理论复杂度$O(mn)$,所以想卡很容易,再加上这题由于差分约束要把所有点都加入队列,所以很容易想到如果构造一条链,连边全部反过来,$1leftarrow 2leftarrow ...leftarrow n$,显然会跑接近$n^2$次,不过数据里并没有故意卡,倒是有一个顺着正向的链的data,我这里采用直接顺着加入队列就没有问题了。但是实际上还是可以卡的?另外,有的点明显spfa被卡,但是有数据连了-1的自环,可以提前特判结束。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 #include<cmath>
     6 #include<queue>
     7 #define dbg(x) cerr << #x << " = " << x <<endl
     8 #define dbg2(x,y) cerr<< #x <<" = "<< x <<"  "<< #y <<" = "<< y <<endl
     9 using namespace std;
    10 typedef long long ll;
    11 typedef double db;
    12 typedef pair<int,int> pii;
    13 template<typename T>inline T _min(T A,T B){return A<B?A:B;}
    14 template<typename T>inline T _max(T A,T B){return A>B?A:B;}
    15 template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,1):0;}
    16 template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,1):0;}
    17 template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;}
    18 template<typename T>inline T read(T&x){
    19     x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1;
    20     while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x;
    21 }
    22 const int N=1e5+7;
    23 struct thxorz{int to,nxt,w;}G[N<<1];
    24 int Head[N],tot,inq[N],rel[N];
    25 int n,m;
    26 ll ans;
    27 inline void Addedge(int x,int y,int z){G[++tot].to=y,G[tot].nxt=Head[x],Head[x]=tot,G[tot].w=z;}
    28 queue<int> q;
    29 ll dis[N];
    30 #define y G[j].to
    31 inline bool spfa(){
    32     for(register int i=1;i<=n;++i)q.push(i),inq[i]=1,dis[i]=1;
    33     while(!q.empty()){
    34         int x=q.front();q.pop();inq[x]=0;//dbg2(x,dis[x]);
    35         for(register int j=Head[x];j;j=G[j].nxt)if(MAX(dis[y],dis[x]+G[j].w)){
    36             rel[y]=rel[x]+1;if(rel[y]>=n)return 0;
    37             if(!inq[y])inq[y]=1,q.push(y);
    38         }
    39     }
    40     return 1;
    41 }
    42 #undef y
    43 int main(){//freopen("1.in","r",stdin);//freopen("test.ans","w",stdout);
    44     read(n),read(m);
    45     for(register int i=1,opt,x,y;i<=m;++i){
    46         read(opt),read(x),read(y);
    47         if(opt==1)Addedge(x,y,0),Addedge(y,x,0);
    48         else if(opt==2)Addedge(x,y,1);
    49         else if(opt==3)Addedge(y,x,0);
    50         else if(opt==4)Addedge(y,x,1);
    51         else Addedge(x,y,0);
    52         if((opt==2||opt==4)&&x==y){printf("-1
    ");return 0;}
    53     }
    54     if(spfa()){
    55         for(register int i=1;i<=n;++i)ans+=dis[i];
    56         printf("%lld
    ",ans);
    57     }
    58     else puts("-1");
    59     return 0;
    60 }
    View Code

    2019.11.01UPD:

    关于正权图或者负权图的差分约束有更快的线性做法。

    以最长路为例,如果图上只有正权边和零边,那么,只要看有没有环是正环,也就是说,对于每一条正边,如果他在一个SCC里,那显然就是正环了,直接判无解。否则,有环的话也只能是零环,零环意味着环上所有点值相同(因为一个被更新,所有点最长路顺着一圈都被更新),所以可以把他们缩掉,这样就变成了一张DAG,这时候就可以直接拓扑排序更新最大值了,最后每个点所在SCC的值就是他的解了。复杂度线性。当然,环上有正有负就不好整了。spfa又死了。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cstring>
     4 #include<algorithm>
     5 #include<cmath>
     6 #include<queue>
     7 #define dbg(x) cerr << #x << " = " << x <<endl
     8 #define dbg2(x,y) cerr<< #x <<" = "<< x <<"  "<< #y <<" = "<< y <<endl
     9 using namespace std;
    10 typedef long long ll;
    11 typedef double db;
    12 typedef pair<int,int> pii;
    13 template<typename T>inline T _min(T A,T B){return A<B?A:B;}
    14 template<typename T>inline T _max(T A,T B){return A>B?A:B;}
    15 template<typename T>inline char MIN(T&A,T B){return A>B?(A=B,1):0;}
    16 template<typename T>inline char MAX(T&A,T B){return A<B?(A=B,1):0;}
    17 template<typename T>inline void _swap(T&A,T&B){A^=B^=A^=B;}
    18 template<typename T>inline T read(T&x){
    19     x=0;int f=0;char c;while(!isdigit(c=getchar()))if(c=='-')f=1;
    20     while(isdigit(c))x=x*10+(c&15),c=getchar();return f?x=-x:x;
    21 }
    22 const int N=1e5+7;
    23 int n,m;
    24 ll ans;
    25 struct thxorz{
    26     int head[N],nxt[N<<1],to[N<<1],from[N<<1],w[N<<1],tot;
    27     inline void add(int x,int y,int z){to[++tot]=y,from[tot]=x,nxt[tot]=head[x],head[x]=tot,w[tot]=z;}
    28 }G1,G2;
    29 int dfn[N],low[N],stk[N],instk[N],bel[N],scc,tim,top;
    30 #define y G1.to[j]
    31 void tarjan(int x){
    32     dfn[x]=low[x]=++tim,stk[++top]=x,instk[x]=1;
    33     for(register int j=G1.head[x];j;j=G1.nxt[j]){
    34         if(!dfn[y])tarjan(y),MIN(low[x],low[y]);
    35         else if(instk[y])MIN(low[x],dfn[y]);
    36     }
    37     if(low[x]==dfn[x]){
    38         int tmp;++scc;
    39         do instk[tmp=stk[top--]]=0,bel[tmp]=scc;while(tmp^x);
    40     }
    41 }
    42 #undef y
    43 queue<int> q;
    44 int dis[N],deg[N];
    45 #define y G2.to[j]
    46 inline void topo(){
    47     for(register int i=1;i<=scc;++i){dis[i]=1;if(!deg[i])q.push(i);}
    48     while(!q.empty()){
    49         int x=q.front();q.pop();
    50         for(register int j=G2.head[x];j;j=G2.nxt[j]){
    51             MAX(dis[y],dis[x]+G2.w[j]);
    52             if(!(--deg[y]))q.push(y);
    53         }
    54     }
    55 }
    56 #undef y
    57 int main(){//freopen("1.in","r",stdin);//freopen("test.ans","w",stdout);
    58     read(n),read(m);
    59     for(register int i=1,opt,x,y;i<=m;++i){
    60         read(opt),read(x),read(y);
    61         if(opt==1)G1.add(x,y,0),G1.add(y,x,0);
    62         else if(opt==2)G1.add(x,y,1);
    63         else if(opt==3)G1.add(y,x,0);
    64         else if(opt==4)G1.add(y,x,1);
    65         else G1.add(x,y,0);
    66     }
    67     for(register int i=1;i<=n;++i)if(!dfn[i])tarjan(i);
    68     for(register int t=1;t<=G1.tot;++t){
    69         int x=G1.from[t],y=G1.to[t];
    70         if(bel[x]==bel[y]&&G1.w[t]){puts("-1");return 0;}
    71         else if(bel[x]^bel[y])G2.add(bel[x],bel[y],G1.w[t]),++deg[bel[y]];
    72     }
    73     topo();
    74     for(register int i=1;i<=n;++i)ans+=dis[bel[i]];
    75     printf("%lld
    ",ans);
    76     return 0;
    77 }
    tarjan!
  • 相关阅读:
    IDEA 启动项目报错 Error:java: java.lang.OutOfMemoryError: GC overhead limit exceeded
    JetBrains 里不为人知的秘密(8) -- 插件篇
    ant-design-vue 之upload 文件上传
    PHP获取IPv4地址
    监控制定程序的CPU和内存开销
    Python字符串转bool函数
    JetsonNano国内环境配置
    局域网主机定时ping实现监控
    无root权限crontab间接实现守护进程
    2020/3/31
  • 原文地址:https://www.cnblogs.com/saigyouji-yuyuko/p/11637241.html
Copyright © 2011-2022 走看看