PKUWC 2018 Day 2 简要题解
*注意:题面请移步至loj查看。
怎么PKU和THU都编了一些假算法,然后求正确率[汗]。
之前听说PKUWC 2017考了五道dp,看来是真的。[完蛋.jpg]
这三道题,2道概率,1道期望,和佬题组成一样。。。
(我只是把PKUWC 2017在loj后3题做了,是不是全是Day 2就不知道了)
Problem A 随机算法
题目大意
给定一个图。随机一个排列$p_{1}p_2p_3\cdots p_n$。假设当前有一个独立集集合$S$,从小到大枚举$i$,尝试将$p_i$加入$S$中。问得到最大独立集的概率。
每个点有三个状态:被考虑其在独立集中,被考虑且不再独立集中,还未被考虑。
设$f_S$表示,当独立集状态为$S$的概率。
每次我们选择独立集中的点的时候,把新增加的不可能在独立集中的点在排列中的位置也考虑了。
这样一个状态,我们就能表示上面三种点了。
时间复杂度$O(n2^n)$
Code
1 /** 2 * loj 3 * Problem#2540 4 * Accepted 5 * Time: 623ms 6 * Memory: 10876k 7 */ 8 #include <iostream> 9 #include <cstdlib> 10 #include <cstdio> 11 using namespace std; 12 typedef bool boolean; 13 14 const int Mod = 998244353; 15 16 const int N = 20, S = 1 << N; 17 18 int add(int a, int b) { 19 return ((a += b) >= Mod) ? (a - Mod) : (a); 20 } 21 22 int mul(int a, int b) { 23 return a * 1ll * b % Mod; 24 } 25 26 void exgcd(int a, int b, int& x, int& y) { 27 if (!b) 28 x = 1, y = 0; 29 else { 30 exgcd(b, a % b, y, x); 31 y -= (a / b) * x; 32 } 33 } 34 35 int inv(int a, int n) { 36 int x, y; 37 exgcd(a, n, x, y); 38 return (x < 0) ? (x + n) : (x); 39 } 40 41 int n, m; 42 int bit[S]; 43 int g[N], f[S]; 44 boolean indep[S]; 45 int fac[N], _fac[N], adj[S]; 46 47 inline void init() { 48 scanf("%d%d", &n, &m); 49 for (int i = 0, u, v; i < m; i++) { 50 scanf("%d%d", &u, &v); 51 --u, --v; 52 g[u] |= (1 << v); 53 g[v] |= (1 << u); 54 } 55 } 56 57 int permu(int n, int m) { 58 if (n < m) 59 return 0; 60 return mul(fac[n], _fac[n - m]); 61 } 62 63 inline void solve() { 64 int all = 1 << n; 65 indep[0] = true; 66 for (int i = 1; i < all; i++) 67 for (int j = 0; j < n && !indep[i]; j++) 68 if (((i >> j) & 1) && indep[i ^ (1 << j)] && !(g[j] & i)) 69 indep[i] = true; 70 71 for (int i = 1; i < all; i++) 72 bit[i] = bit[i >> 1] + (i & 1); 73 74 int mx = 0; 75 for (int i = 1; i < all; i++) 76 if (indep[i] && bit[i] > mx) 77 mx = bit[i]; 78 79 adj[0] = 0; 80 for (int i = 1; i < all; i++) 81 for (int j = 0; j < n && !adj[i]; j++) 82 if ((i >> j) & 1) 83 adj[i] = adj[i ^ (1 << j)] | g[j]; 84 85 fac[0] = 1; 86 for (int i = 1; i <= n; i++) 87 fac[i] = mul(fac[i - 1], i); 88 _fac[n] = inv(fac[n], Mod); 89 for (int i = n; ~i; i--) 90 _fac[i - 1] = mul(_fac[i], i); 91 92 f[0] = 1; 93 for (int s = 0; s < all - 1; s++) { 94 if (!f[s] || !indep[s]) 95 continue; 96 int ban = adj[s] | s; 97 for (int i = 0; i < n; i++) { 98 if (((ban >> i) & 1)) 99 continue; 100 f[s | (1 << i)] = add(f[s | (1 << i)], mul(f[s], permu(n - bit[ban] - 1, bit[g[i]] - bit[ban & g[i]]))); 101 } 102 } 103 int res = 0; 104 for (int i = 1; i < all; i++) 105 if (indep[i] && bit[i] == mx) 106 res = add(res, f[i]); 107 res = mul(res, _fac[n]); 108 printf("%d\n", res); 109 } 110 111 int main() { 112 init(); 113 solve(); 114 return 0; 115 }
Problem B 猎人杀
题目大意
(原题题意过于简洁不需要大意)
考虑求出在$1$被消灭掉后,剩下的集合为$S$的概率$f_S$。
发现直接求它不好求,考虑求出剩下的集合包含$S$时的概率$g_S$。
我们希望求$f_{\varnothing}$。
我们发现这个分母的变化会使得事情变得毒瘤起来,考虑被打死的人不会退出游戏,这样不会改变要求的概率。
设$A = \sum_{i = 1}^{n}w_i$,$B = \sum_{i \in S}w_i$,那么有:
$\begin{align} g_s &= \frac{w_1}{A}\sum_{i = 0}^{\infty}\left ( 1 - \frac{B + w_1}{A} \right )^{i} \\ &= \frac{w_{1}}{A}\cdot \frac{1}{1 - \left(1 - \frac{B +w_1}{A} \right )} \\ &= \frac{w_1}{B + w_1 }\end{align}$
因为剩下的集合不同的两个事件是互斥的,所以有:
$\begin{align} g_s = \sum_{t\subseteq U\wedge s\subseteq t}f_{t}\end{align}$
反演得到:
$\begin{align} f_s = \sum_{t\subseteq U\wedge s\subseteq t} (-1)^{|t| - |s|}g_t\end{align}$
因为$w$的和不超过$10^5$,考虑求出项$\frac{w_{1}}{w_{1} + B}$的系数。
直接构造一个函数$G(x) = \prod_{i = 2}^{n}(1 - x^{w_i})$,用分治NTT求出来就好了。
Code
1 /** 2 * loj 3 * Problem#2541 4 * Accepted 5 * Time: 940ms 6 * Memory: 7084k 7 */ 8 #include <bits/stdc++.h> 9 using namespace std; 10 typedef bool boolean; 11 #define ll long long 12 using std::reverse; 13 14 // Polynomial Template 15 16 template <typename T> 17 void pfill(T* pst, const T* ped, T val) { 18 for ( ; pst != ped; *(pst++) = val); 19 } 20 21 template <typename T> 22 void pcopy(T* pst, const T* ped, T* pval) { 23 for ( ; pst != ped; *(pst++) = *(pval++)); 24 } 25 26 const int N = 262144; 27 const int M = 998244353; 28 const int inv2 = (M + 1) >> 1; 29 const int g = 3; 30 const int bzmax = 18; 31 32 void debug(int *f, int len) { 33 for (int i = 0; i < len; i++) 34 std :: cerr << f[i] << " "; 35 std :: cerr << '\n'; 36 } 37 38 int add(int a, int b) { 39 return ((a += b) >= M) ? (a - M) : (a); 40 } 41 42 int sub(int a, int b) { 43 return ((a -= b) < 0) ? (a + M) : (a); 44 } 45 46 int mul(int a, int b) { 47 return a * 1ll * b % M; 48 } 49 50 int qpow(int a, int p) { 51 if (p < 0) 52 p += M - 1; 53 int rt = 1, pa = a; 54 for ( ; p; p >>= 1, pa = pa * 1ll * pa % M) 55 if (p & 1) 56 rt = rt * 1ll * pa % M; 57 return rt; 58 } 59 60 class NTT { 61 public: 62 int gn[1 << (bzmax + 2)], rgn[1 << (bzmax + 2)]; 63 64 NTT() { 65 int *p = gn, *_p = rgn; 66 for (int i = 0, l = 2; i < bzmax; i++, l <<= 1) { 67 // gn[i] = qpow(g, (M - 1) / l); 68 // rgn[i] = qpow(g, -(M - 1) / l); 69 int tmp = 1; 70 int wn = qpow(g, (M - 1) >> (i + 1)); 71 int _wn = qpow(g, - ((M - 1) >> (i + 1))); 72 for (int j = 0; j < l; j++) 73 *(p++) = tmp, tmp = mul(tmp, wn); 74 tmp = 1; 75 for (int j = 0; j < l; j++) 76 *(_p++)= tmp, tmp = mul(tmp, _wn); 77 // cerr << i << " " << cnt << '\n'; 78 } 79 } 80 81 void rader(int *f, int len) { 82 for (int i = 1, j = len >> 1, k; i < len - 1; i++, j += k) { 83 if (i < j) 84 std::swap(f[i], f[j]); 85 for (k = len >> 1; j >= k; j -= k, k >>= 1); 86 } 87 } 88 89 void operator () (int* f, int len, int sign) { 90 rader(f, len); 91 const int *p = ((sign > 0) ? (gn) : (rgn)); 92 for (int b = 2; b <= len; b <<= 1) { 93 // int wn = ((sign > 0) ? (gn[t]) : (rgn[t])), w = 1, hb = b >> 1; 94 const int * const pc = p; 95 const int hb = b >> 1; 96 for (int i = 0; i < len; i += b, p = pc) 97 for (int j = i; j < i + hb; j++) { 98 int a = f[j], b = *p++ * 1ll * f[j + hb] % M; 99 f[j] = add(a, b); 100 f[j + hb] = sub(a, b); 101 } 102 p = p + b; 103 } 104 105 if (sign == -1) { 106 int invlen = qpow(len, M - 2); 107 for (int i = 0; i < len; i++) 108 f[i] = f[i] * 1ll * invlen % M; 109 } 110 } 111 112 int correctLen(int n) { 113 int t = 1; 114 for ( ; t < n; t <<= 1); 115 return t; 116 } 117 118 }NTT; 119 120 typedef vector<int> Poly; 121 122 Poly operator * (Poly a, Poly b) { 123 int n = (signed) (a.size() + b.size()) - 1; 124 if (n < 64) { 125 Poly rt (n); 126 pfill(rt.data(), rt.data() + n, 0); 127 for (unsigned i = 0; i < a.size(); i++) 128 for (unsigned j = 0; j < b.size(); j++) 129 rt[i + j] = add(rt[i + j], mul(a[i], b[j])); 130 return rt; 131 } 132 int t = NTT.correctLen(n); 133 Poly rt (t); 134 a.resize(t), b.resize(t); 135 NTT(a.data(), t, 1); 136 NTT(b.data(), t, 1); 137 for (int i = 0; i < t; i++) 138 rt[i] = mul(a[i], b[i]); 139 NTT(rt.data(), t, -1); 140 rt.resize(n); 141 return rt; 142 } 143 144 Poly dividing(int n, int* a) { 145 if (n == 1) { 146 Poly rt (*a + 1); 147 pfill(rt.data(), rt.data() + *a + 1, 0); 148 rt[*a] = M - 1, rt[0] = 1; 149 return rt; 150 } 151 int sum = 0, L = 0, s = *a; 152 for (int i = 0; i < n; i++) 153 sum += a[i]; 154 sum >>= 1; 155 for (int i = 1; i < n; s += a[i], i++) 156 if (s + a[i] > sum) { 157 L = i; 158 break; 159 } 160 return dividing(L, a) * dividing(n - L, a + L); 161 } 162 163 int n, s; 164 int *w; 165 166 inline void init() { 167 scanf("%d", &n); 168 w = new int[(n + 1)]; 169 for (int i = 0; i < n; i++) { 170 scanf("%d", w + i); 171 if (i > 0) 172 s += w[i]; 173 } 174 } 175 176 inline void solve() { 177 if (n == 1) { 178 puts("1"); 179 return; 180 } 181 sort(w + 1, w + n); 182 Poly p = dividing(n - 1, w + 1); 183 int res = 1; 184 for (int i = 1; i <= s; i++) 185 if (p[i]) 186 res = add(res, mul(mul(w[0], p[i]), qpow(add(w[0], i), -1))); 187 printf("%d\n", res); 188 } 189 190 int main() { 191 init(); 192 solve(); 193 return 0; 194 }
Problem C 随机游走
题目大意
(原题题意过于简洁不需要大意)
Solution 1
设$f_{s, i}$表示当前在点$i$,走完$s$中包含的点期望还需要多少步。
对于非根节点的方程可以化为$f_{s,i} = k\cdot f_{s', fa_i} + b$的形式,然后两遍dfs就行了。
Code
1 /** 2 * loj 3 * Problem#2542 4 * Accepted 5 * Time: 997ms 6 * Memory: 37116k 7 */ 8 #include <iostream> 9 #include <cstdlib> 10 #include <cstdio> 11 #include <vector> 12 using namespace std; 13 typedef bool boolean; 14 15 const int Mod = 998244353; 16 17 int add(int a, int b) { 18 return ((a += b) >= Mod) ? (a - Mod) : (a); 19 } 20 21 int sub(int a, int b) { 22 return ((a -= b) < 0) ? (a + Mod) : (a); 23 } 24 25 int mul(int a, int b) { 26 return a * 1ll * b % Mod; 27 } 28 29 void exgcd(int a, int b, int& x, int& y) { 30 if (!b) 31 x = 1, y = 0; 32 else { 33 exgcd(b, a % b, y, x); 34 y -= (a / b) * x; 35 } 36 } 37 38 int inv(int a, int n) { 39 int x, y; 40 exgcd(a, n, x, y); 41 return (x < 0) ? (x + n) : (x); 42 } 43 44 const int N = 18, S = 1 << N; 45 46 typedef class Value { 47 public: 48 int k, b; 49 50 Value(int k = 0, int b = 0) : k(k), b(b) { } 51 }Value; 52 53 int n, q, X; 54 int msk_all; 55 Value f[S][N]; 56 vector<int> g[N]; 57 58 inline void init() { 59 scanf("%d%d%d", &n, &q, &X), --X; 60 for (int i = 1, u, v; i < n; i++) { 61 scanf("%d%d", &u, &v); 62 --u, --v; 63 g[u].push_back(v); 64 g[v].push_back(u); 65 } 66 msk_all = (1 << n) - 1; 67 } 68 69 void dfs1(int p, int S, int Fa) { 70 int deg = (signed) g[p].size(), coef = deg, cont = deg, nS; 71 for (int i = 0, e; i < deg; i++) { 72 if ((e = g[p][i]) == Fa) 73 continue; 74 nS = S & (msk_all ^ (1 << e)); 75 dfs1(e, S, p); 76 if (nS ^ S) { 77 cont = add(cont, f[nS][e].b); 78 } else { 79 coef = sub(coef, f[S][e].k); 80 cont = add(cont, f[S][e].b); 81 } 82 } 83 84 if ((1 << p) & S) 85 return; 86 87 int _coef = inv(coef, Mod); 88 if (p == X) { 89 f[S][p] = Value(0, mul(cont, _coef)); 90 } else { 91 f[S][p] = Value(_coef, mul(_coef, cont)); 92 } 93 } 94 95 void dfs2(int p, int S, int Fa) { 96 if (p != X && !((1 << p) & S)) { 97 int nS = S & (msk_all ^ (1 << Fa)); 98 f[S][p].b = add(mul(f[S][p].k, f[nS][Fa].b), f[S][p].b); 99 f[S][p].k = 0; 100 } 101 102 for (int i = 0, e; i < (signed) g[p].size(); i++) { 103 if ((e = g[p][i]) == Fa) 104 continue; 105 dfs2(e, S, p); 106 } 107 } 108 109 inline void solve() { 110 for (int s = 1; s <= msk_all; s++) { 111 dfs1(X, s, -1); 112 dfs2(X, s, -1); 113 } 114 115 int K, msk; 116 while (q--) { 117 scanf("%d", &K); 118 msk = 0; 119 for (int i = 0, x; i < K; i++) 120 scanf("%d", &x), msk |= (1 << (x - 1)); 121 msk = msk & (msk_all ^ (1 << X)); 122 printf("%d\n", f[msk][X].b); 123 } 124 } 125 126 int main() { 127 init(); 128 solve(); 129 return 0; 130 }
Solution 2
用最值反演把问题转化成求走到$s$中任意一个点的期望。
假设当前考虑的集合是$s$,设$f_i$表示当前在点$i$,走到$s$中任意一个点的期望。
然后同样的做法dp一下就行了。
Code
1 /** 2 * loj 3 * Problem#2542 4 * Accepted 5 * Time: 617ms 6 * Memory: 1232k 7 */ 8 #include <iostream> 9 #include <cstdlib> 10 #include <cstdio> 11 #include <vector> 12 using namespace std; 13 typedef bool boolean; 14 15 const int Mod = 998244353; 16 17 int add(int a, int b) { 18 return ((a += b) >= Mod) ? (a - Mod) : (a); 19 } 20 21 int sub(int a, int b) { 22 return ((a -= b) < 0) ? (a + Mod) : (a); 23 } 24 25 int mul(int a, int b) { 26 return a * 1ll * b % Mod; 27 } 28 29 void exgcd(int a, int b, int& x, int& y) { 30 if (!b) 31 x = 1, y = 0; 32 else { 33 exgcd(b, a % b, y, x); 34 y -= (a / b) * x; 35 } 36 } 37 38 int inv(int a, int n) { 39 int x, y; 40 exgcd(a, n, x, y); 41 return (x < 0) ? (x + n) : (x); 42 } 43 44 const int N = 18, S = 1 << N; 45 46 typedef class Value { 47 public: 48 int k, b; 49 50 Value(int k = 0, int b = 0) : k(k), b(b) { } 51 }Value; 52 53 int n, q, X; 54 int msk_all; 55 Value f[N]; 56 int F[S]; 57 vector<int> g[N]; 58 59 inline void init() { 60 scanf("%d%d%d", &n, &q, &X), --X; 61 for (int i = 1, u, v; i < n; i++) { 62 scanf("%d%d", &u, &v); 63 --u, --v; 64 g[u].push_back(v); 65 g[v].push_back(u); 66 } 67 msk_all = (1 << n) - 1; 68 } 69 70 void dfs1(int p, int S, int Fa) { 71 int deg = (signed) g[p].size(), coef = deg, cont = deg, nS; 72 for (int i = 0, e; i < deg; i++) { 73 if ((e = g[p][i]) == Fa) 74 continue; 75 nS = S & (1 << e); 76 dfs1(e, S, p); 77 if (!nS) { 78 coef = sub(coef, f[e].k); 79 cont = add(cont, f[e].b); 80 } 81 } 82 83 int _coef = inv(coef, Mod); 84 if ((1 << p) & S) 85 f[p] = Value(0, 0); 86 else if (p == X) { 87 f[p] = Value(0, mul(cont, _coef)); 88 } else { 89 f[p] = Value(_coef, mul(_coef, cont)); 90 } 91 } 92 93 void dfs2(int p, int S, int Fa) { 94 if (p != X && !((1 << p) & S)) { 95 f[p].b = add(mul(f[p].k, f[Fa].b), f[p].b); 96 f[p].k = 0; 97 } 98 99 for (int i = 0, e; i < (signed) g[p].size(); i++) { 100 if ((e = g[p][i]) == Fa) 101 continue; 102 dfs2(e, S, p); 103 } 104 } 105 106 inline void solve() { 107 for (int s = 1, bit = 0; s <= msk_all; s++) { 108 if (s & (1 << X)) { 109 F[s] = 0; 110 continue; 111 } 112 dfs1(X, s, -1); 113 dfs2(X, s, -1); 114 F[s] = f[X].b, bit = 0; 115 for (int s0 = s; s0; s0 -= (s0 & (-s0)), bit ^= 1); 116 if (!bit) 117 F[s] = Mod - F[s]; 118 } 119 120 for (int i = 0; i < n; i++) 121 for (int j = 0; j <= msk_all; j++) 122 if ((j >> i) & 1) 123 F[j] = add(F[j], F[j ^ (1 << i)]); 124 125 int K, msk; 126 while (q--) { 127 scanf("%d", &K); 128 msk = 0; 129 for (int i = 0, x; i < K; i++) 130 scanf("%d", &x), msk |= (1 << (x - 1)); 131 msk = msk & (msk_all ^ (1 << X)); 132 printf("%d\n", F[msk]); 133 } 134 } 135 136 int main() { 137 init(); 138 solve(); 139 return 0; 140 }