[BZOJ 1797][BZOJ 3258]最小割的唯一性判定

问题一:是否存在一个最小代价路径切断方案,其中该道路被切断? 问题二:是否对任何一个最小代价路径切断方案,都有该道路被切断? 现在请你回答这两个问题。


最小割唯一性判定


jcvb:

在残余网络上跑tarjan求出所有SCC,记id[u]为点u所在SCC的编号。显然有id[s]!=id[t](否则s到t有通路,能继续增广)。

①对于任意一条满流边(u,v),(u,v)能够出现在某个最小割集中,当且仅当id[u]!=id[v];
②对于任意一条满流边(u,v),(u,v)必定出现在最小割集中,当且仅当id[u]==id[s]且id[v]==id[t]。

<==将每个SCC缩成一个点,得到的新图就只含有满流边了。那么新图的任一s-t割都对应原图的某个最小割,从中任取一个把id[u]和id[v]割开的割即可证明。


<==:假设将(u,v)的边权增大,那么残余网络中会出现s->u->v->t的通路,从而能继续增广,于是最大流流量(也就是最小割容量)会增大。这即说明(u,v)是最小割集中必须出现的边。




#include 
   
   
    
    
#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
       
       
#include 
       
       
         #include 
        
          #define maxn 200000 using namespace std; const int inf = 0x7fffffff; struct Edge{ int to, next, w; }edge[maxn]; int from[maxn], to[maxn]; int h[maxn], cnt = 1; void add(int u, int v, int w){ cnt ++; edge[cnt].to = v; edge[cnt].next = h[u]; edge[cnt].w = w; h[u] = cnt; swap(u, v);w = 0; cnt ++; edge[cnt].to = v; edge[cnt].next = h[u]; edge[cnt].w = w; h[u] = cnt; } int n, m, S, T; queue 
         
           Q; int d[4010]; bool BFS(){ Q.push(S); memset(d, -1, sizeof d); d[S] = 0; while(!Q.empty()){ int u = Q.front();Q.pop(); for(int i = h[u]; i; i = edge[i].next){ if(!edge[i].w)continue; int v = edge[i].to; if(d[v] == -1){ d[v] = d[u] + 1; Q.push(v); } } }return d[T] != -1; } int DFS(int x, int a){ if(a == 0 || x == T)return a; int used = 0, f; for(int i = h[x]; i; i = edge[i].next){ int v = edge[i].to; if(d[v] == d[x] + 1){ f = DFS(v, min(edge[i].w, a - used)); edge[i].w -= f; edge[i ^ 1].w += f; used += f; if(used == a)return used; } } if(!used)d[x] = -1; return used; } void Dinic(){ int ans = 0; while(BFS()) ans += DFS(S, inf); } int low[maxn], pre[maxn], dfs_clock, sccno[maxn], scc_cnt; stack 
          
            s; void Tarjan(int u){ low[u] = pre[u] = ++ dfs_clock; s.push(u); for(int i = h[u]; i; i = edge[i].next){ if(!edge[i].w)continue; int v = edge[i].to; if(!pre[v])Tarjan(v), low[u] = min(low[u], low[v]); else if(!sccno[v])low[u] = min(low[u], pre[v]); } if(low[u] == pre[u]){ ++ scc_cnt; for(;;){ int v = s.top();s.pop(); sccno[v] = scc_cnt; if(v == u)break; } } } int main(){ freopen("mincut.in" ,"r",stdin ); freopen("mincut.out","w",stdout); scanf("%d%d%d%d", &n, &m, &S, &T); int w; for(int i = 1; i <= m; i ++){ scanf("%d%d%d", &from[i], &to[i], &w); add(from[i], to[i], w); } Dinic(); for(int i = 1; i <= n; i ++) if(!pre[i])Tarjan(i); for(int i = 1; i <= m; i ++){ int u = from[i], v = to[i]; if(edge[i << 1].w){ printf("0 0\n"); continue; } if(sccno[u] == sccno[v]) printf("0 0\n"); else if(sccno[u] == sccno[S] && sccno[v] == sccno[T]) printf("1 1\n"); else printf("1 0\n"); } return 0; } 
           
          
         
       
      
      
     
     
    
    
   
   

3258: 秘密任务
Description


Alice听说在一片神奇的大陆MagicLand,有一个古老的传说…… 
 很久很久以前,那个时候 MagicStates共和国刚刚成立。 反对新政府的势力虽已被镇压,但仍然在暗地活动。这一次,情报局得到了一个令人震惊的消息,被软禁在首都府邸中的Frank ——著名的反对派领袖,秘密逃出首都,去往反对派的大本营。根据相关的情报,Frank计划通过城市之间 发达的高速公路,经过最短的路程抵达目的地。不妨将 MagicStates共和国简化为由N个城市,M条高速公路构成的连通的无向图,首都为城市1,反对派的大本营为城市N。 
每条高速公路连接两个不同的城市,且路程是已知的。而Frank选择了一条从城市1到城市N的最短路径作为他的逃跑路线。为了阻止Frank,共和国总统决定在某些城市的高速公路的出入口设立检查 点,在Frank经过检查点时将他逮捕。 
举例来说,如果有一条高速公路连接城市u和城市v,在这条公路的城市u或城市v的出入口设立检查点,那么Frank经过高速公路时就会被发现。特别的是,由于城市N实际上处在反对派的控制下,所以不能在城市N设立检查点。


然而在任何城市设立检查点都需要一定的费用。更具体的,若在城市 u设立k个检查点,就要花费 Au乘以k的代价,其中Au是城市u的相关参数。值得注意的是,这个代价与这k个检查点具体设在哪些公路的出入口无关,于是,总统责令情报局拟定一个方案,花费最小的代价使得无论Frank选择哪条最短路线,都会在(除城市N以外)某个城市的高速公路出入口被发现。读到这里,Alice很想知道阻止Frank所需要花费的最小代价,并且她还希 望知道最优方案是否是唯一的。只好再请你帮助她了。 
注意,我们称两个方案不同当且仅当存在某城市k,两种方案中在城市 k的检查点的设置(而不仅是数目)是不同的。 
注意,输入文件包含多组测试数据。


BZOJ3258秘密任务
最小割唯一性判定
这才是真正的最小割唯一性判定
注意一个二元组不能随便建。
要解方程。
此题要拆边,对应两条边两个点的属性
不要任性T^T



#include 
    
    
     
     
#include 
     
     
      
      
#include 
      
      
       
       
#include 
       
       
        
        
#include 
        
        
          #include 
         
           #define maxn 10010 using namespace std; typedef long long ll; int n, m, S, T, size; struct Edge{int to, next, w;}edge[100010]; int h[maxn], cnt, val[maxn], fr[100010], to[100010], C; void add(int u, int v, int w){ C ++, fr[C] = u, to[C] = v; cnt ++; edge[cnt].to = v; edge[cnt].next = h[u]; edge[cnt].w = w; h[u] = cnt; swap(u, v);w = 0; cnt ++; edge[cnt].to = v; edge[cnt].next = h[u]; edge[cnt].w = w; h[u] = cnt; } queue 
          
            Q; namespace spfa{ struct Edge_{int to, next, dis;}G[100010]; int cnt, h[maxn]; ll dis[maxn], disT[maxn]; bool vis[maxn]; void addG(int u, int v, int d){ cnt ++; G[cnt].to = v; G[cnt].next = h[u]; G[cnt].dis = d; h[u] = cnt; } void work(){ cnt = 0; memset(h, 0, sizeof h); int u, v, d; for(int i = 1; i <= m; i ++){ scanf("%d%d%d", &u, &v, &d); addG(u, v, d); addG(v, u, d); } memset(dis, 0x7f, sizeof dis); memset(vis, 0, sizeof vis); dis[S] = 0;Q.push(S); while(!Q.empty()){ int u = Q.front();Q.pop(); for(int i = h[u]; i; i = G[i].next){ int v = G[i].to; if(dis[v] > dis[u] + G[i].dis){ dis[v] = dis[u] + G[i].dis; if(!vis[v])vis[v] = true, Q.push(v); } } vis[u] = false; } memset(disT, 0x7f, sizeof disT); memset(vis, 0, sizeof vis); disT[T] = 0;Q.push(T); while(!Q.empty()){ int u = Q.front();Q.pop(); for(int i = h[u]; i; i = G[i].next){ int v = G[i].to; if(disT[v] > disT[u] + G[i].dis){ disT[v] = disT[u] + G[i].dis; if(!vis[v])vis[v] = true, Q.push(v); } } vis[u] = false; } size = n; for(int i = 1; i < n; i ++) for(int j = h[i]; j; j = G[j].next){ int v = G[j].to; if(dis[T] == dis[i] + G[j].dis + disT[v]){ ++ size; add(i, size, val[i]); add(size, v, val[v]); //add(i, v, min(val[i], val[v])); } } } } const int inf = 0x7fffffff; int d[maxn]; bool BFS(){ memset(d, -1, sizeof d); d[S] = 0; Q.push(S); while(!Q.empty()){ int u = Q.front();Q.pop(); for(int i = h[u]; i; i = edge[i].next){ if(!edge[i].w)continue; int v = edge[i].to; if(d[v] == -1){ d[v] = d[u] + 1; Q.push(v); } } }return d[T] != -1; } ll DFS(int x, ll a){ if(a == 0 || x == T)return a; ll used = 0, f; for(int i = h[x]; i; i = edge[i].next){ int v = edge[i].to; if(d[v] == d[x] + 1){ f = DFS(v, min(a - used, (ll)edge[i].w)); edge[i].w -= f; edge[i ^ 1].w += f; used += f; if(used == a)return used; } } if(!used)d[x] = -1; return used; } ll Dinic(){ ll ans = 0; while(BFS()) ans += DFS(S, inf); return ans; } int sccno[maxn], pre[maxn], low[maxn], dfs_clock, scc_cnt; stack 
           
             s; void Tarjan(int u){ pre[u] = low[u] = ++ dfs_clock; s.push(u); for(int i = h[u]; i; i = edge[i].next){ if(!edge[i].w)continue; int v = edge[i].to; if(!pre[v])Tarjan(v), low[u] = min(low[u], low[v]); else if(!sccno[v])low[u] = min(low[u], pre[v]); } if(low[u] == pre[u]){ ++ scc_cnt; for(;;){ int v = s.top();s.pop(); sccno[v] = scc_cnt; if(v == u)break; } } } int main(){ //freopen("secret.in", "r", stdin); //freopen("secret.out", "w", stdout); int test; scanf("%d", &test); while(test --){ C = 0; scanf("%d%d", &n, &m); for(int i = 1; i < n; i ++) scanf("%d", &val[i]); val[n] = 0x7fffffff; memset(h, 0, sizeof h); cnt = 1; S = 1, T = n; spfa::work(); ll ans = Dinic(); memset(pre, 0, sizeof pre); memset(low, 0, sizeof low); memset(sccno, 0, sizeof sccno); scc_cnt = dfs_clock = 0; for(int i = 1; i <= size; i ++) if(!pre[i])Tarjan(i); for(int i = 1; i <= C; i ++){ int u = fr[i], v = to[i]; if(edge[i << 1].w || sccno[u] == sccno[v])continue; if(sccno[u] == sccno[S] && sccno[v] == sccno[T]) continue; printf("No %lld\n", ans); goto Mark; } printf("Yes %lld\n", ans); Mark:; } return 0; } 
            
           
          
        
       
       
      
      
     
     
    
    


重点是看S和T是否联通

我们的思想是Tarjan找桥边。

如果存在可能不是最小割上的边

那么就不唯一。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值