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 A

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 B

 

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

 

posted @ 2019-08-10 06:32  Gekoo  阅读(142)  评论(0编辑  收藏  举报