NOIP模拟测试15
T2读题跪掉了好可惜 QAQ
Problem A:建设城市
插板加容斥搞个不停,得到柿子:$\sum \limits_{i=0} (-1) ^ {i}C_{m - ik - 1} ^ {n-1} \times C_n^i$。
预处理阶乘和阶乘逆元,ning干。
1 #include <bits/stdc++.h> 2 #define ll long long 3 4 const int D = 998244353; 5 ll n, m, k, fac[10000007], inv[10000007], ans; 6 7 ll Qpow(ll x, ll b) { 8 ll ret = 1; 9 for (; b; b >>= 1, x = x * x % D) 10 if (b & 1) ret = ret * x % D; 11 return ret; 12 } 13 14 void init(int x) { 15 fac[0] = fac[1] = 1; 16 for (int i = 2; i <= x; i++) fac[i] = fac[i - 1] * i % D; 17 for (int i = 1; i <= x; i++) inv[i] = Qpow(fac[i], D - 2); 18 } 19 20 ll C(int n, int m) { 21 return fac[n] * inv[m] % D * inv[n - m] % D; 22 } 23 24 signed main() { 25 scanf("%lld%lld%lld", &n, &m, &k); 26 if (n > m || m > n * k) { 27 puts("0"); 28 return 0; 29 } 30 init(std::max(n, m)); 31 ans += C(m - 1, n - 1); 32 for (int i = 1; i <= n; i++) { 33 if (m - i * k - 1 < n - 1) break; 34 ll tmp = C(n, i) * C(m - i * k - 1, n - 1) % D; 35 if (i & 1) ans = (ans - tmp + D) % D; 36 else ans = (ans + tmp) % D; 37 } 38 printf("%lld\n", ans % D); 39 return 0; 40 }
Problem B:轰炸行动
读错题,读成一条边的两边不能同时轰炸,这肯定就可以染色嘛,获得了10分的好成绩半个机房都看错了
真正的题意:两点联通就不能同时轰炸。
那我就没话讲了。Tarjan跑出SCC,拓扑跑个最长链。
1 #include <bits/stdc++.h> 2 3 const int N = 10000005; 4 int n, m; 5 std::vector<int> E[N], G[N]; 6 7 int dfn[N], low[N], stk[N], tp, num, scc, val[N], pos[N], ind[N], f[N], ans; 8 bool ins[N]; 9 10 void Tarjan(int x) { 11 low[x] = dfn[x] = ++num; 12 ins[stk[++tp] = x] = 1; 13 for (auto y : E[x]) { 14 if (!dfn[y]) { 15 Tarjan(y); 16 low[x] = std::min(low[x], low[y]); 17 } else if (ins[y]) { 18 low[x] = std::min(low[x], dfn[y]); 19 } 20 } 21 if (low[x] == dfn[x]) { 22 ++scc; 23 int y; 24 do { 25 y = stk[tp--], ins[y] = 0; 26 val[scc]++, pos[y] = scc; 27 } while (y != x); 28 } 29 } 30 31 void topsort() { 32 std::queue<int> q; 33 for (int i = 1; i <= scc; i++) { 34 if (!ind[i]) { 35 q.push(i); 36 f[i] = val[i]; 37 } 38 } 39 while (!q.empty()) { 40 int x = q.front(); 41 q.pop(); 42 for (auto y : G[x]) { 43 --ind[y]; 44 f[y] = std::max(f[y], f[x] + val[y]); 45 if (!ind[y]) { 46 q.push(y); 47 } 48 } 49 } 50 for (int i = 1; i <= scc; i++) 51 ans = std::max(ans, f[i]); 52 } 53 54 signed main() { 55 scanf("%d%d", &n, &m); 56 for (int i = 1, a, b; i <= m; i++) { 57 scanf("%d%d", &a, &b); 58 E[a].push_back(b); 59 } 60 for (int i = 1; i <= n; i++) { 61 if (!dfn[i]) Tarjan(i); 62 } 63 for (int x = 1; x <= n; x++) { 64 for (auto y : E[x]) { 65 if (pos[x] != pos[y]) { 66 G[pos[x]].push_back(pos[y]); 67 ind[pos[y]]++; 68 } 69 } 70 } 71 topsort(); 72 printf("%d\n", ans); 73 return 0; 74 }
Problem C:石头剪刀布
20分特判必拿。感觉像是能状压,但搞了半天搞不出来,感觉不可做,和AI下五子棋。
题解过于谔谔,n那么小不是没原因的(
设f[i][j][k][l]为i+j+k步i个石头j个剪刀k个布,下一步为l的概率。然后就发现不可求。
再开一个辅助数组g[i][j][k]表示i+j+k步i个石头j个剪刀k个布的概率。
g[]的转移:$g[i][j][[k] += g[i-1][j][k] * r[t] + g[i][j-1][k] * s[t] + g[i][j][k-1] * p[t]$。
利用g[]可以把f[]也转移了:$f[i][j][k][l] += f[i-1][j][k][l]*r[t]+f[i][j-1][k][l]*s[t]+f[i][j][k-1][l]*s[t]+g[i][j][k]*(r[t]+p[t]+s[t])$.
到这里使劲想想还是能想出来的,但此时发现看不懂std。Orz
std最主要看不懂的就是那个第5层循环。其实是这样的。
首先std没有开g[],直接用f[][][][0]当g[]用。我们要求f[i][j][k][l],首先要先从之前的状态把已有概率继承,柿子的前半段就是在干这个。
if (x) f[j][k][l][x] += f[j][k][l][0] * QAQ[i][x]
这一行只有j+k+l!=i的时候才会有可能执行。它存在的意义就是用g[]更新状态,是之前柿子右半部分。
if (j) f[j][k][l][x] += f[j-1][k][l][x] * QAQ[i][1]; if (k) f[j][k][l][x] += f[j][k-1][l][x] * QAQ[i][2]; if (l) f[j][k][l][x] += f[j][k][l-1][x] * QAQ[i][3];
这三行得分成x = 0和x > 0去看。x > 0时仍然是继承。x = 0时则是在更新g[]。
当j+k+l==i时,目标状态都已经更新完毕了,这时用目标状态给下一个状态更新一下g[]。
1 #include <bits/stdc++.h> 2 3 int n; 4 double QAQ[105][5], f[60][60][60][10], C[70][70], ans; 5 6 signed main() { 7 scanf("%d", &n); 8 for (int i = 1, r, p, s; i <= n; i++) { 9 scanf("%d%d%d", &r, &p, &s); 10 QAQ[i][1] = (double) r / 300.0; 11 QAQ[i][2] = (double) s / 300.0; 12 QAQ[i][3] = (double) p / 300.0; 13 } 14 C[0][0] = C[1][0]= C[1][1] = 1.0; 15 for (int i = 2; i <= n; i++) { 16 C[i][0] = 1.0; 17 for (int j = 1; j <= i; j++) { 18 C[i][j] = C[i-1][j] + C[i-1][j-1]; 19 } 20 } 21 f[0][0][0][0] = 1.0; 22 for (int i = 1; i <= n; i++) { 23 for (int j = i; j >= 0; j--) { 24 for (int k = i - j; k >= 0; k--) { 25 for (int l = i - j - k; l >= 0; l--) { 26 for (int x = ((j + k + l == i) ? 0 : 3); x >= 0; x--) { 27 if (j) f[j][k][l][x] += f[j-1][k][l][x] * QAQ[i][1]; 28 if (k) f[j][k][l][x] += f[j][k-1][l][x] * QAQ[i][2]; 29 if (l) f[j][k][l][x] += f[j][k][l-1][x] * QAQ[i][3]; 30 if (x) f[j][k][l][x] += f[j][k][l][0] * QAQ[i][x]; 31 } 32 } 33 } 34 } 35 } 36 for (int i = 0; i < n; i++) { 37 for (int j = 0; i + j < n; j++) { 38 for (int k = 0; i + j + k < n; k++) { 39 double tmp1 = f[i][j][k][1] + 3.0 * f[i][j][k][2]; 40 double tmp2 = f[i][j][k][2] + 3.0 * f[i][j][k][3]; 41 double tmp3 = f[i][j][k][3] + 3.0 * f[i][j][k][1]; 42 ans += std::max(std::max(tmp1, tmp2), tmp3) / 43 (C[n][i+j+k] * (n - i - j - k)); 44 } 45 } 46 } 47 printf("%.12lf\n", ans); 48 return 0; 49 }