1. 程式人生 > >【LOJ2321】「清華集訓 2017」無限之環

【LOJ2321】「清華集訓 2017」無限之環

【題目連結】

【思路要點】

  • 先說這道題的正解:
  • 將棋盤看做一張二分圖,每一條邊拆成兩個點,分別屬於二分圖的一邊。
  • 我們需要做一件類似於匹配的事情,同一條邊的兩側或是都沒有管道,或是都有管道。
  • 通過合適的建邊我們能夠用最小費用最大流來解決本題。
  • 時間複雜度 O(MinCostFlow(NM,NM))O(MinCostFlow(N*M,N*M))
  • 但自從筆者從考場回來就一直在思考本題的插頭DP做法的複雜度是不是能夠被證明更優,因為我們發現實際上合法的插頭數少之又少。
  • 遺憾的是筆者並不能證明其更優的複雜度,但筆者將考場上的插頭DP程式碼的狀態開成了64位整型,就通過了本題,而且似乎比大部分費用流的做法要快。
  • 因此筆者相信在本題中插頭DP有著低於理論時間複雜度上界( O(NM2NM)O(NM*2^{\sqrt{NM}}) )的複雜度。
  • 以下程式碼實現的是插頭DP。
  • 時間複雜度 O(S)O(S) ,其中 SS 為合法的狀態數。
  • Upd:Upd: 更新了網路流做法。

【程式碼】

//Flow Version
#include<bits/stdc++.h>
using namespace std;
const int MAXQ = 1e7 + 5;
const int MAXP = 1e5 + 5;
const int MAXN = 2e3 + 5;
const
int INF = 2e9; typedef long long ll; typedef long double ld; typedef unsigned long long ull; template <typename T> void chkmax(T &x, T y) {x = max(x, y); } template <typename T> void chkmin(T &x, T y) {x = min(x, y); } template <typename T> void read(T &x) { x = 0; int
f = 1; char c = getchar(); for (; !isdigit(c); c = getchar()) if (c == '-') f = -f; for (; isdigit(c); c = getchar()) x = x * 10 + c - '0'; x *= f; } template <typename T> void write(T x) { if (x < 0) x = -x, putchar('-'); if (x > 9) write(x / 10); putchar(x % 10 + '0'); } template <typename T> void writeln(T x) { write(x); puts(""); } struct edge {int dest, flow, pos, cost; }; vector <edge> a[MAXP]; int n, m, s, t, tot, flow, cost, goal; int dist[MAXP], path[MAXP], home[MAXP]; int point[MAXN][MAXN][4], cnt[2]; void FlowPath() { int p = t, ans = INF; while (p != s) { ans = min(ans, a[path[p]][home[p]].flow); p = path[p]; } flow += ans; cost += ans * dist[t]; p = t; while (p != s) { a[path[p]][home[p]].flow -= ans; a[p][a[path[p]][home[p]].pos].flow += ans; p = path[p]; } } bool spfa() { static int q[MAXQ]; static bool inq[MAXP]; static int l = 0, r = 0; for (int i = 0; i <= r; i++) dist[q[i]] = INF; q[l = r = 0] = s, dist[s] = 0, inq[s] = true; while (l <= r) { int tmp = q[l]; for (unsigned i = 0; i < a[tmp].size(); i++) if (a[tmp][i].flow != 0 && dist[tmp] + a[tmp][i].cost < dist[a[tmp][i].dest]) { dist[a[tmp][i].dest] = dist[tmp] + a[tmp][i].cost; path[a[tmp][i].dest] = tmp; home[a[tmp][i].dest] = i; if (!inq[a[tmp][i].dest]) { q[++r] = a[tmp][i].dest; inq[q[r]] = true; } } l++, inq[tmp] = false; } return dist[t] != INF; } void addedge(int x, int y, int z, int c) { a[x].push_back((edge) {y, z, a[y].size(), c}); a[y].push_back((edge) {x, 0, a[x].size() - 1, -c}); } int main() { read(n), read(m); s = 0, t = tot = 1; for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { point[i][j][0] = ++tot; point[i][j][1] = ++tot; point[i][j][2] = ++tot; point[i][j][3] = ++tot; } //0 : up, 1 : right, 2 : down, 3 : left. for (int i = 1; i <= n; i++) for (int j = 1; j <= m; j++) { int x; read(x); int tmp = ++tot, Cnt = 0, tnp = x; int up = point[i][j][0]; int Right = point[i][j][1]; int down = point[i][j][2]; int Left = point[i][j][3]; while (tnp) {Cnt += tnp & 1, tnp >>= 1; } if ((i + j) & 1) { addedge(s, tmp, Cnt, 0); if (x & 1) addedge(tmp, up, 1, 0), cnt[0]++; if (x & 2) addedge(tmp, Right, 1, 0), cnt[0]++; if (x & 4) addedge(tmp, down, 1, 0), cnt[0]++; if (x & 8) addedge(tmp, Left, 1, 0), cnt[0]++; if (x == 1) { addedge(tmp, Right, 1, 1); addedge(tmp, down, 1, 2); addedge(tmp, Left, 1, 1); } if (x == 2) { addedge(tmp, up, 1, 1); addedge(tmp, down, 1, 1); addedge(tmp, Left, 1, 2); } if (x == 4) { addedge(tmp, up, 1, 2); addedge(tmp, Right, 1, 1); addedge(tmp, Left, 1, 1); } if (x == 8) { addedge(tmp, up, 1, 1); addedge(tmp, Right, 1, 2); addedge(tmp, down, 1, 1); } if (x == 14) { addedge(Right, up, 1, 1); addedge(down, up, 1, 2); addedge(Left, up, 1, 1); } if (x == 13) { addedge(up, Right, 1, 1); addedge(down, Right, 1, 1); addedge(Left, Right, 1, 2); } if (x == 11) { addedge(up, down, 1, 2); addedge(Right, down, 1, 1); addedge(Left, down, 1, 1); } if (x == 7) { addedge(up, Left, 1, 1); addedge(Right, Left, 1, 2); addedge(down, Left, 1, 1); } if (x == 3) { addedge(up, down, 1, 1); addedge(Right, Left, 1, 1); } if (x == 6) { addedge(down, up, 1, 1); addedge(Right, Left, 1, 1); } if (x == 12) { addedge(down, up, 1, 1); addedge(Left, Right, 1, 1); } if (x == 9) { addedge(up, down, 1, 1); addedge(Left, Right, 1, 1); } } else { addedge(tmp, t, Cnt, 0); if (x & 1) addedge(up, tmp, 1, 0), cnt[1]++; if (x & 2) addedge(Right, tmp, 1, 0), cnt[1]++; if (x & 4) addedge(down, tmp, 1, 0), cnt[1]++; if (x & 8) addedge(Left, tmp, 1, 0), cnt[1]++; if (x == 1) { addedge(Right, tmp, 1, 1); addedge(down, tmp, 1, 2); addedge(Left, tmp, 1, 1); } if (x == 2) { addedge(up, tmp, 1, 1); addedge(down, tmp, 1, 1); addedge(Left, tmp, 1, 2); } if (x == 4) { addedge(up, tmp, 1, 2); addedge(Right, tmp, 1, 1); addedge(Left, tmp, 1, 1); } if (x == 8) { addedge(up, tmp, 1, 1); addedge(Right, tmp, 1, 2); addedge(down, tmp, 1, 1); } if (x == 14) { addedge(up, Right, 1, 1); addedge(up, down, 1, 2);

相關推薦

LOJ2321清華集訓 2017無限

【題目連結】 【思路要點】 先說這道題的正解: 將棋盤看做一張二分圖,每一條邊拆成兩個點,分別屬於二分圖的一邊。 我們需要做一件類似於匹配的事情,同一條邊的兩側或是都沒有管道,或是都有管道。 通過合適的建邊我們能夠用最小費用最大流來解決本題。 時間複雜度

LOJ2330清華集訓 2017榕樹

【題目連結】 點選開啟連結 【思路要點】 首先,樹是二分圖,只有一側的點可能成為心。 維護每一棵子樹會產生的向下推動的次數可能的最大值

LOJ2321清華集訓 2017無限

無限之環 題目描述 曾經有一款流行的遊戲,叫做InfinityLoopInfinityLoop,先來簡單的介紹一下這個遊戲: 遊戲在一個 n×mn×m的網格狀棋盤上進行,其中有些小方格中會有水管,水管可能在方格某些方向的邊界的中點有介面,所

清華集訓 2017無限

無限之WA https://www.luogu.org/problemnew/show/P4003 本題如果知道是網路流的話,其實建圖不算特別神奇,但是比較麻煩。 資料範圍過大,插頭dp不能處理,而且是一個網格圖,考慮網路流。 先看是不是二分圖? 每個格子只會和相鄰四個格子發生關係 所以,黑白染色

LOJ2322清華集訓 2017Hello world!

【題目連結】 點選開啟連結 【思路要點】 一個 1

LOJ2328清華集訓 2017避難所

【題目連結】 點選開啟連結 【思路要點】 令 x

LOJ2326清華集訓 2017簡單資料結構

【題目連結】 點選開啟連結 【思路要點】 注意到答案是 O

LOJ2323清華集訓 2017小 Y 和地鐵

【題目連結】 點選開啟連結 【思路要點】 很不錯的腦洞題。 附上官方題解。 時間複雜度 O

LOJ2324清華集訓 2017小 Y 和二叉樹

【題目連結】 點選開啟連結 【思路要點】 答案的第一位一定是編號最小的度數不為 3

LOJ2329清華集訓 2017我的生命已如風中殘燭

【題目連結】 點選開啟連結 【思路要點】 一個直觀的思路是模擬該過程,當路上遇到環的時候通過類似取模的手段加速。 注意到每繞一個環

LOJ2331清華集訓 2017某位歌姬的故事

【題目連結】 點選開啟連結 【思路要點】 注意到若一個位置被兩種音高 a

LOJ2327清華集訓 2017福若格斯

【題目連結】 點選開啟連結 【思路要點】 M

LOJ2320清華集訓 2017生成樹計數

【題目連結】【思路要點】連上\(a_i\)的限制,題目要求的實際上是\(\sum_{T}\prod_{i=1}^{N}a_i^{d_i}*d_i^{M}\sum_{i=1}^{N}d_i^{M}\)。我們知道樹的Prufer序列與樹點的度數密切相關,因此考慮使用Prufer序

loj#2330. 清華集訓 2017榕樹樹形dp

傳送門 解題思路: 先考慮根是否可行,即步數是否能抵消完。 考慮w[x]w[x]表示xx的子樹內最少的消剩下的點數。 觀察發現,最難消的肯定是sizesize最大的兒子,設為 yy ,而且如果

LOJ#2330. 清華集訓 2017榕樹心 -樹形dp

題解 先考慮根的情況(Subtask3Subtask3)。 根的每個兒子及其構成的子樹之間可以互相抵消。 設rem[i]rem[i]表示以ii為根的子樹最少的不能互相抵消的點數。 那

清華集訓2017榕樹

name clear 方向 世界 oid 可行性 pre lin size 「清華集訓2017」榕樹之心 “已經快是嚴冬了,榕樹的葉子還沒落呢……” “榕樹是常綠樹,是看不到明顯的落葉季節的……” “唉……想不到已經七年了呢。榕樹還是當年的榕樹,你卻不是當年的你了……”

LOJ#2320. 清華集訓 2017生成樹計數

rac res 然而 除了 加法 wap OS 代碼 reg 題解 我,理解題解,用了一天 我,卡常數,又用了一天 到了最後,我才發現,我有個加法取模,寫的是while(c >= MOD) c -= MOD 我把while改成if,時間,少了 六倍。 六倍。 六倍!!

LOJ2325清華集訓 2017小Y和恐怖的奴隸主(期望概率+矩陣快速冪)

LOJ2325「清華集訓 2017」小Y和恐怖的奴隸主 題意: "A fight? Count me in!" 要打架了,算我一個。 "Everyone, get in here!" 所有人,都過來! 小Y是一個喜歡玩遊戲的OIer。一天,她正在

期望+矩陣乘法LOJ2325 [清華集訓 2017] 小 Y 和恐怖的奴隸主

【題目】 原題地址 BOSS \text{BOSS} BOSS初始有一個

UOJ #36清華集訓2014瑪裡苟斯

這怎麼想得到啊......... UOJ #36 題意:求隨機一個集合的子集的異或和的$k$次方的期望值,保證答案$ \lt 2^{63},1 \leq k \leq 5$ $ Solution:$ 首先考慮$ k=1$的時候怎麼做:如果某位上有$ 1$則有$ \frac{1}{2}$的