PKUWC 2017 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 A

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 B

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 }
Problem C

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 }
Problem C

 

posted @ 2018-12-09 20:03  阿波罗2003  阅读(...)  评论(... 编辑 收藏