Luogu3163 [CQOI2014]危桥 ---- 网络流 及 一个细节的解释

Luogu3163 [CQOI2014]危桥

题意

有$n$个点和$m$条边,有些边可以无限次数的走,有些边这辈子只能走两次,给定两个起点和终点$a_1 --> a_2$(起点 --> 终点)和$b_1 --> b_2$(起点 --> 终点),询问是否可以让$a_1 --> a_2$往返$a_n$次,让$b_1 --> b_2$往返$b_n$次

题解

思路

思路还是比较好想的,就是原图连双向边,然后炒鸡源汇连$a_n*2$和$b_n*2$判断满流是否为$(a_n+b_n)*2$。

但是这样可以吗,不可以。

解决方案是把$b_1 和 b_2$反过来跑。

为什么呢?因为有可能$a_1$的流量可能会跑到$b_2$处。

于是下面给出说明。

反正就会发生这么一种情况:

我们发现这张图的流量是满了,但是中间旁边就发现流量根本忽略了危桥,而且$a_1$的流量跑到了$b_2$那里。

所以我们直接把第二条路反过来,就是这样

然后发现就不会出现那种情况

一个细节

有人会说“如果危桥正向边反向边都有2的流量呢”?

然而我们证明一下发现这是不可能的。

证明:

如果出现了正向边反向边都有2的流量(或者一个为2一个为1),那么就说明$a_1 --> a_2$和$b_1 --> b_2$在危桥的路径上相反。也就是提取他们的路径刚好相反。

那么这种情况其实就是图1的情况,$a_1 --> a_2$要经过危桥的正向边$b_1 --> b_2$要经过危桥的反向边,然后就会发现$a_1$和$b_2$连在一起了,然后这个时候最小割应该是靠近源点的$(a_n+b_n)*2$而不是中间的$INF+INF+2*2$,而且中间根本不会有流量!所以这种情况下不会去流中间的边(根据最小割)。

代码如下:

然而由于数组没开足够大挂了半天......

  1 #include <cstdio>
  2 #include <cctype>
  3 #include <cstring>
  4 #include <iostream>
  5
  6 //User‘s Lib
  7
  8 using namespace std;
  9
 10 // #define DEBUG_PORT
 11 #define DEBUG
 12
 13 #ifdef ONLINE_JUDGE
 14 #undef DEBUG_PORT
 15 #undef DEBUG
 16 #endif
 17
 18 #ifdef DEBUG_PORT
 19 #if __cplusplus >= 201103L
 20 #ifdef DEBUG
 21 template<typename T>
 22 extern inline void Debug(T tar){
 23     cerr << tar << endl;
 24 }
 25 template<typename Head, typename T, typename... Tail>
 26 extern inline void Debug(Head head, T mid, Tail... tail){
 27     cerr << head << ‘ ‘;
 28     Debug(mid, tail...);
 29 }
 30 #else
 31 # pragma GCC diagnostic push
 32 # pragma GCC diagnostic ignored "-Wunused-parameter"
 33 template<typename Head, typename T, typename... Tail>
 34 extern inline void Debug(Head head, T mid, Tail... tail){
 35     return ;
 36 }
 37 # pragma GCC diagnostic pop
 38 # pragma message "Warning : pragma used"
 39 #endif
 40 #else
 41 # pragma message "Warning : C++11 Not Use"
 42 #ifdef DEBUG
 43 template <typename T>
 44 extern inline void Debug(T tar){
 45     cerr << tar << endl;
 46 }
 47 #else
 48 # pragma GCC diagnostic push
 49 # pragma GCC diagnostic ignored "-Wunused-parameter"
 50 template <typename T>
 51 extern inline void Debug(T tar){
 52     return ;
 53 }
 54 # pragma GCC diagnostic pop
 55 # pragma message "Warning : pragma used"
 56 #endif
 57 #endif
 58 #else
 59 # pragma GCC diagnostic push
 60 # pragma GCC diagnostic ignored "-Wunused-parameter"
 61 template<typename Head, typename T, typename... Tail>
 62 extern inline void Debug(Head head, T mid, Tail... tail){
 63     return ;
 64 }
 65 template <typename T>
 66 extern inline void Debug(T tar){
 67     return ;
 68 }
 69 # pragma GCC diagnostic pop
 70 # pragma message "Warning : pragma used"
 71 #endif
 72
 73 char buf[11111111], *pc = buf;
 74
 75 extern inline void Main_Init(){
 76     static bool INITED = false;
 77     if(INITED) fclose(stdin), fclose(stdout);
 78     else {
 79         fread(buf, 1, 11111111, stdin);
 80         INITED = true;
 81     }
 82 }
 83
 84 static inline int read(){
 85     int num = 0;
 86     char c, sf = 1;
 87     while(isspace(c = *pc++));
 88     if(c == 45) sf = -1, c = *pc ++;
 89     while(num = num * 10 + c - 48, isdigit(c = *pc++));
 90     return num * sf;
 91 }
 92
 93 namespace LKF{
 94     template <typename T>
 95     extern inline T abs(T tar){
 96         return tar < 0 ? -tar : tar;
 97     }
 98     template <typename T>
 99     extern inline void swap(T &a, T &b){
100         T t = a;
101         a = b;
102         b = t;
103     }
104     template <typename T>
105     extern inline void upmax(T &x, const T &y){
106         if(x < y) x = y;
107     }
108     template <typename T>
109     extern inline void upmin(T &x, const T &y){
110         if(x > y) x = y;
111     }
112     template <typename T>
113     extern inline T max(T a, T b){
114         return a > b ? a : b;
115     }
116     template <typename T>
117     extern inline T min(T a, T b){
118         return a < b ? a : b;
119     }
120 }
121
122 //Source Code
123
124 /*
125 把原图往返看成经过两次
126 所以原图中起点和终点只有一个方向的流量这样子
127 然后危桥建单向边就可以了
128 同时为了防止出现什么起点和起点间的交易
129 所以跑一次之后再交换跑一次即可
130 证明博客:https://www.cnblogs.com/CreeperLKF/p/9176605.html
131 */
132
133 const int MAXK = 55;///WTF...MAXN = 50炸了
134 const int MAXN = 66;
135 const int MAXM = 6666;
136 const int INF = 0x3f3f3f3f;
137
138 int n, m, s = MAXN - 10, t = s + 1;
139
140 struct Queue{
141     int s, t;
142     int q[MAXN];
143     Queue(){s = 1, t = 0;}
144     inline void clear(){
145         s = 1, t = 0;
146     }
147     inline bool empty(){
148         return s > t;
149     }
150     inline int size(){
151         return t - s + 1;
152     }
153     inline void push(int tar){
154         q[++ t] = tar;
155     }
156     inline int front(){
157         return q[s];
158     }
159     inline void pop(){
160         s ++;
161     }
162 };
163
164 struct Graph{
165     int tot;
166     int beginx[MAXN], endx[MAXM], nxt[MAXM], res[MAXM];
167     Graph(){
168         tot = 1;
169     }
170     inline void Init(){
171         tot = 1;
172         memset(beginx, 0, sizeof(beginx));
173     }
174     inline void add_edge(int u, int v, int r){
175         // Debug(u, "->", v, "[label = \"", r, "\"]");//Debug...
176         nxt[++ tot] = beginx[u], beginx[u] = tot, endx[tot] = v, res[tot] = r;
177         nxt[++ tot] = beginx[v], beginx[v] = tot, endx[tot] = u, res[tot] = 0;
178     }
179 };
180
181 struct ISap{
182     Graph g;
183     Queue mession;
184     int max_f;
185     int cur[MAXN], d[MAXN], num[MAXN], pre[MAXN];
186     inline void bfs(){
187         mession.clear();
188         mession.push(t);
189         memset(d, 0, sizeof(d));
190         memset(num, 0, sizeof(num));
191         d[t] = 1;
192         int u, v;
193         while(!mession.empty()){
194             u = mession.front();
195             mession.pop();
196             num[d[u]] ++;
197             for(int i = g.beginx[u]; i; i = g.nxt[i]){
198                 v = g.endx[i];
199                 if(!d[v] && g.res[i ^ 1]){
200                     d[v] = d[u] + 1;
201                     mession.push(v);
202                 }
203             }
204         }
205     }
206     inline int dfs(int u, int now_f){
207         if(u == t) return now_f;
208         int ret_f = 0;
209         for(int &i = cur[u]; i; i = g.nxt[i]){
210             int v = g.endx[i];
211             if(g.res[i] && d[u] == d[v] + 1){
212                 int ret = dfs(v, min(g.res[i], now_f));
213                 ret_f += ret, now_f -= ret;
214                 g.res[i] -= ret, g.res[i ^ 1] += ret;
215                 if(d[s] >= MAXN - 4 || !now_f) return ret_f;
216             }
217         }
218         if(-- num[d[u]] == 0) d[s] = MAXN - 4;
219         ++ num[++ d[u]];
220         cur[u] = g.beginx[u];
221         return ret_f;
222     }
223     inline int ISAP(){
224         bfs();
225         max_f = 0;
226         memcpy(cur, g.beginx, sizeof(cur));
227         while(d[s] < MAXN - 5)
228             max_f += dfs(s, INF);
229         return max_f;
230     }
231 }isap;
232
233 int a1, a2, an, b1, b2, bn;
234 int matrix[MAXK][MAXK];
235
236 int main(){
237     Main_Init();
238     while((n = read()) > 0){
239         a1 = read() + 1, a2 = read() + 1, an = read() << 1;
240         b1 = read() + 1, b2 = read() + 1, bn = read() << 1;
241         memset(matrix, 0, sizeof(matrix));
242         for(int i = 1; i <= n; i++){
243             while(isspace(*pc ++));
244             pc --;
245             for(int j = 1; j <= n; j++){
246                 char c = *pc ++;
247                 switch(c){
248                     case ‘O‘: matrix[i][j] = 1; break;
249                     case ‘N‘: matrix[i][j] = 2; break;
250                 }
251             }
252         }
253
254         isap.g.Init();
255         isap.g.add_edge(s, a1, an), isap.g.add_edge(a2, t, an);
256         isap.g.add_edge(s, b1, bn), isap.g.add_edge(b2, t, bn);
257         for(int i = 1; i <= n; i++)
258             for(int j = 1; j <= n; j++)
259                 if(matrix[i][j])
260                     isap.g.add_edge(i, j, matrix[i][j] & 1 ? 2 : INF);
261         if(isap.ISAP() != an + bn){
262             puts("No");
263             continue;
264         }
265
266         isap.g.Init();
267         isap.g.add_edge(s, a1, an), isap.g.add_edge(a2, t, an);
268         isap.g.add_edge(s, b2, bn), isap.g.add_edge(b1, t, bn);
269         for(int i = 1; i <= n; i++)
270             for(int j = 1; j <= n; j++)
271                 if(matrix[i][j])
272                     isap.g.add_edge(i, j, matrix[i][j] & 1 ? 2 : INF);
273         puts(isap.ISAP() == an + bn ? "Yes" : "No");//大小写......
274     }
275     Main_Init();
276     return 0;
277 }

原文地址:https://www.cnblogs.com/CreeperLKF/p/9176605.html

时间: 2024-10-10 22:26:28

Luogu3163 [CQOI2014]危桥 ---- 网络流 及 一个细节的解释的相关文章

bzoj3504: [Cqoi2014]危桥 网络流

一种网络流建图的思路吧,改天最好整理一波网络流建图思路 1 #include <bits/stdc++.h> 2 using namespace std; 3 int n,h,t,a1,a2,an,b1,b2,bn,flow,now;char ch; 4 int dis[52],l[52],d[52][52];char c[52][52]; 5 char getch() 6 { 7 for(ch=getchar();ch!='O' && ch!='N' && c

bzoj千题计划137:bzoj [CQOI2014]危桥

http://www.lydsy.com/JudgeOnline/problem.php?id=3504 往返n遍,即单向2*n遍 危桥流量为2,普通桥流量为inf 原图跑一遍最大流 交换b1,b2再跑一遍最大流 如果两次的结果都等于(an+bn)*2 则可以 证明参见http://www.cnblogs.com/chenyushuo/p/5139556.html #include<queue> #include<cstdio> #include<cstring> #i

3504: [Cqoi2014]危桥

3504: [Cqoi2014]危桥 链接 分析: 首先往返的可以转化为全是“往”,那么只要将容量除以2即可. 然后S向a1连边容量为an(除以2之前为2*an),S向a2连边容量为an,b1,b2向T连边容量为bn.原图上的边,建双向边保存. 这样会存在从a1流向b2的流量,当然也有b1流向a2的流量,考虑如何判断这种情况. 将b1,b2交换,然后重新跑一遍,判断是否满流即可. 第一遍最大流的时候,假设a1->b2流了x的流量,那么有 a1->a2:an-x, a1->b2:x, b1

P3163 [CQOI2014]危桥

传送门 我是看不出这玩意儿和网络流有什么关系-- 我们把图中的所有边都当成无向边加入图中,容量为\(inf\) 危桥的容量为\(2\) 从源点到\(a1,b1\)连边容量为\(an*2\),\(a2,b2\)到汇点连边容量\(bn*2\),相当于一次把两边都走完 然后跑一遍看看是否满流即可 然而这样会有一个问题,就是最终求得的最大流是\(a1\)流向\(b2\)或\(a2\)流向\(b1\) 于是再从源点向\(a1\)和\(b2\)连边,\(a2\)和\(b1\)向汇点连边,再跑一遍看看是否满流

【BZOJ 3504】[Cqoi2014]危桥

Description Alice和Bob居住在一个由N座岛屿组成的国家,岛屿被编号为0到N-1.某些岛屿之间有桥相连,桥上的道路是双 向的,但一次只能供一人通行.其中一些桥由于年久失修成为危桥,最多只能通行两次.Alice希望在岛屿al和a2之间往返an次(从al到a2再从a2 到al算一次往返).同时,Bob希望在岛屿bl和b2之间往返bn次.这个过程中,所有危桥最多通行两次,其余的桥可以无限次通行.请问Alice和 Bob能完成他们的愿望吗? Input 本题有多组测试数据. 每组数据第一

[Cqoi2014]危桥 (两遍网络流)

题目链接 1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 inline int read() 5 { 6 int x=0,f=1;char ch=getchar(); 7 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 8 while(ch>='0'&&ch<='9'){x=x*10+ch-'0

bzoj3504: [Cqoi2014]危桥

题意:给出一个图,有的边可以无限走,有的只能走两次(从一头到另一头为一次),给定两个起点以及对应的终点以及对应要走几个来回,求判断是否能完成. 先来一个NAIVE的建图:直接限制边建为容量1,无限制为INF,按照原图连,然后跑最大流就可以了. 可惜这样还不够,因为有可能有一部分流量不是对应的起点流过来的,即两条路有流量交换,这样就不一定可以满足题意了. 解决方法是:再跑一遍网络流,但是建图要改变一下,即将a路线的起点终点调换一下(当然b也可以),再接着跑,如果仍然满足则是真的有解. 证明看了网上

bzoj 3504: [Cqoi2014]危桥

1 #include<cstdio> 2 #include<iostream> 3 #include<cstring> 4 #define M 100009 5 #define inf 2139062143 6 using namespace std; 7 int n,a1,a2,an,b1,b2,bn,tot,cnt=1,T,ans,head[M],d[M],q[2*M],next[10*M],u[10*M],v[10*M]; 8 char ch[60][60]; 9

bzoj3504: [Cqoi2014]危桥--最大流

题目大意:给张无向图,有两个人a,b分别从各自的起点走向各自的终点,走A,B个来回,图里有些边只能走两次,求问是否能满足a,b的需求 按照题目给的表建图 S连a1,b1 a2,b2连T 跑最大流看是否满流 为了防止a1跑到b2的情况 第二遍 S连a1,b2 a2,b1连T 若还是满流说明没问题 1 #include<stdio.h> 2 #include<string.h> 3 #include<algorithm> 4 #include<queue> 5