UR 2 密碼鎖
阿新 • • 發佈:2018-04-10
class 在一起 密碼鎖 display mod void urn als turn
\[UR\ \ 2 密碼鎖\]
- \(myy\)的題
- 然而考場上只會\(O(3^N)\)的算法還因為時間問題只寫了\(O(3^N*N)\)然後就被\(32\)位機卡常了只有\(26\)分,還搞得另外兩個題沒檢查都爆零了,
myy:你們是我見過最差的一屆 - 自己垃圾無話可說
- 部分分可以參考競賽圖求\(SCC\)的那種套路來做,就是先設一個什麽\(f[s]\)表示集合\(s\)是\(SCC\)的概率,然後用一個類似背包的東西枚舉子集來轉移(其實這個做法有點強行套上一道以前的題沒有考慮本題的性質
- 然而有更加優美的\(O(2^N*N^2)\)的做法,能夠發現競賽圖縮點後一定形如一條鏈,考慮對鏈上的每一條邊計數,為了方便可以假設最後一個點之後也會連著一條鏈,所以我們可以\(O(2^N)\)
- 考慮進一步優化上面的算法,發現我們一個大小為\(M\)的沒有特殊的邊的集合成為一個前綴的概率是\((\frac{1}{2})^{M*(N-M)}\),每加入一條特殊的邊就是乘上\(2p\),考慮對大小為M的集合一起處理,最後一起乘以\((\frac{1}{2})^{M*(N-M)}\)就好了,然後其實我並不會第\(4\)個\(SubTask\)就直接講正解吧,邊的貢獻可以直接枚舉連通塊來算,然後把每些邊組成的聯通塊先用\(dp\)算出來在取這個集合時邊的貢獻,然後用背包合在一起,最後一起乘上那個\(2\)的多少次冪即可,因為邊數很小,所以連通塊的點數也很小,可以通過
#include <bits/stdc++.h> typedef long long LL; inline int read(int Num = 0, int Flag = 1) { char ch = getchar(); for (; !isdigit(ch); ch = getchar()) if (ch == ‘-‘) Flag = -1; for (; isdigit(ch); ch = getchar()) Num = Num * 10 + ch - ‘0‘; return Num *= Flag; } template <typename T> bool chkmax(T &a, T b) { return a < b? a = b, true : false; } template <typename T> bool chkmin(T &a, T b) { return a > b? a = b, true : false; } const int MAXN = 50 + 5; const int mod = 998244353; const int base = 1e4; int fpm(int x, int e) { int ret = 1; for (; e; e >>= 1) { if (e & 1) ret = (LL)ret * x % mod; x = (LL)x * x % mod; } return ret; } int N, M, mul_num; std::vector<int> G[MAXN]; int from[MAXN], to[MAXN], p[MAXN]; int size, tot; int id[MAXN], occur[MAXN]; void DFS_work(int u, int tot) { id[u] = size ++; occur[u] = tot; for (int i = 0; i < (int)G[u].size(); ++i) { int v = G[u][i]; if (id[v] < 0) DFS_work(v, tot); } } int sum[MAXN][MAXN]; int dp[MAXN][MAXN]; int main() { freopen("random.in", "r", stdin); freopen("random.out", "w", stdout); N = read(), M = read(); mul_num = fpm(base, N * (N-1)); for (int i = 1; i <= M; ++i) { int u = read(), v = read(), w = read(); G[u].push_back(v); G[v].push_back(u); from[i] = u, to[i] = v; p[i] = (LL)w * fpm(base, mod - 2) % mod; } int allblock_size = 0; memset(id, -1, sizeof id); for (int i = 1; i <= N; ++i) { if (id[i] >= 0) continue; size = 0; tot ++; DFS_work(i, tot); allblock_size += size; for (int s = 0; s < 1<<size; ++s) { int coe = 1; for (int j = 1; j <= M; ++j) { if (occur[from[j]] == tot && (((s >> id[from[j]]) ^ (s >> id[to[j]])) & 1)) { coe = coe * 2 % mod; coe = (LL)coe * ((s >> id[from[j]]) & 1? p[j] : mod + 1 - p[j]) % mod; } } int bit = __builtin_popcount(s); sum[tot][bit] = (sum[tot][bit] + coe) % mod; } } dp[0][0] = 1; for (int i = 0; i < tot; ++i) { for (int j = 0; j <= N; ++j) { for (int k = 0; k + j <= N; ++k) dp[i+1][j+k] = (dp[i+1][j+k] + (LL)dp[i][j] * sum[i + 1][k] % mod) % mod; } } int ans = 0; assert(allblock_size == N); for (int i = 1; i <= N; ++i) { ans = (ans + (LL)dp[tot][i] * fpm((mod + 1) / 2, i * (N-i)) % mod) % mod; } printf("%lld\n", (LL)ans * mul_num % mod); return 0; }
UR 2 密碼鎖