zoukankan      html  css  js  c++  java
  • NOIP模拟测试15

    T2读题跪掉了好可惜 QAQ

    Problem A:建设城市

    插板加容斥搞个不停,得到柿子:$sum limits_{i=0} (-1) ^ {i}C_{m - ik - 1} ^ {n-1} imes 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
    ", 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
    ", 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
    ", ans);
    48     return 0;
    49 }
    Problem C
  • 相关阅读:
    备忘录
    中缀表达式转为后缀表达式
    未出现的最小正整数
    摩尔投票算法
    两个等长升序序列找中位数
    Morris二叉树遍历
    2020牛客寒假算法基础集训营5 街机争霸
    2020牛客寒假算法基础集训营5 牛牛战队的比赛地
    2020牛客寒假算法基础集训营2 求函数
    2020牛客寒假算法基础集训营2 建通道
  • 原文地址:https://www.cnblogs.com/gekoo/p/11329806.html
Copyright © 2011-2022 走看看