• 大三第一周训练


    周三

    前段时间没有写日报,而是写专题了

    概率/期望dp专题_Alex Su (*^▽^*)的博客-CSDN博客

    博弈论专题_Alex Su (*^▽^*)的博客-CSDN博客

    现在觉得写日报才每天有收获感,有动力,于是又开始写日报

    P3802 小魔女帕琪(手推概率)

    这题就是手算,算出1~7触发的概率,再算一下2~8触发的概率,发现是一样的,然后就可以推出答案了

    1. #include<bits/stdc++.h>
    2. #define rep(i, a, b) for(int i = (a); i < (b); i++)
    3. #define _for(i, a, b) for(int i = (a); i <= (b); i++)
    4. using namespace std;
    5. int main()
    6. {
    7. double a[10], N;
    8. _for(i, 1, 7) scanf("%lf", &a[i]), N += a[i];
    9. double ans = (N - 6) * 5040;
    10. _for(i, 1, 7) ans *= a[i] / max(N - i + 1, 1.0);
    11. printf("%.3f\n", ans);
    12. return 0;
    13. }

    P1850 [NOIP2016 提高组] 换教室(期望dp+floyed)

    这题的期望比较特殊,它是比较独立的,概率不需要连乘

    也就是说期望体现在两个相邻时间段之间的贡献。这个时候就不是那种比较经典的期望dp了。

    细节上,注意初始化和边界问题。

    floyed的时候注意自己自己到自己为0,我忽略了这个卡了很久。

    1. #include<bits/stdc++.h>
    2. #define rep(i, a, b) for(int i = (a); i < (b); i++)
    3. #define _for(i, a, b) for(int i = (a); i <= (b); i++)
    4. using namespace std;
    5. const int N = 2e3 + 10;
    6. const int M = 300 + 10;
    7. int n, m, v, e, c[N], d[N], g[M][M];
    8. double k[N], dp[N][N][2];
    9. int main()
    10. {
    11. scanf("%d%d%d%d", &n, &m, &v, &e);
    12. _for(i, 1, n) scanf("%d", &c[i]);
    13. _for(i, 1, n) scanf("%d", &d[i]);
    14. _for(i, 1, n) scanf("%lf", &k[i]);
    15. memset(g, 0x3f, sizeof g);
    16. while(e--)
    17. {
    18. int a, b, w;
    19. scanf("%d%d%d", &a, &b, &w);
    20. g[a][b] = g[b][a] = min(g[a][b], w);
    21. }
    22. _for(K, 1, v)
    23. _for(i, 1, v)
    24. _for(j, 1, v)
    25. g[i][j] = min(g[i][j], g[i][K] + g[K][j]);
    26. _for(i, 1, v) g[i][i] = 0; //写弗洛伊德时记得要加上自己和自己为0
    27. _for(i, 0, n)
    28. _for(j, 0, m)
    29. dp[i][j][0] = dp[i][j][1] = 1e9;
    30. dp[1][1][1] = dp[1][0][0] = 0;
    31. _for(i, 2, n)
    32. _for(j, 0, m)
    33. {
    34. int c1 = c[i - 1], c2 = c[i], d1 = d[i - 1], d2 = d[i];
    35. dp[i][j][0] = min(dp[i - 1][j][0] + g[c1][c2], dp[i - 1][j][1]
    36. + k[i - 1] * g[d1][c2] + (1 - k[i - 1]) * g[c1][c2]);
    37. if(j > 0)
    38. dp[i][j][1] = min(dp[i - 1][j - 1][0] + k[i] * g[c1][d2] + (1 - k[i]) * g[c1][c2],
    39. dp[i - 1][j - 1][1] + k[i - 1] * k[i] * g[d1][d2] + (1 - k[i - 1]) * k[i] * g[c1][d2] +
    40. k[i - 1] * (1 - k[i]) * g[d1][c2] + (1 - k[i - 1]) * (1 - k[i]) * g[c1][c2]);
    41. }
    42. double ans = 1e9;
    43. _for(i, 0, m) ans = min(ans, min(dp[n][i][0], dp[n][i][1]));
    44. printf("%.2f\n", ans);
    45. return 0;
    46. }

    E. Vasya and Magic Matrix(dp+前缀和优化)

    令N=nm,从数据范围来看,是一个O(N)的算法

    直接dp显然要O(N^2)

    把式子拆开后发现有很多前缀和,所以可以预处理前缀和来优化

    1. #include<bits/stdc++.h>
    2. #define rep(i, a, b) for(int i = (a); i < (b); i++)
    3. #define _for(i, a, b) for(int i = (a); i <= (b); i++)
    4. using namespace std;
    5. typedef long long ll;
    6. const int N = 1e6 + 10;
    7. const int mod = 998244353;
    8. ll dp[N], xx[N], x[N], y[N], yy[N], f[N];
    9. struct node
    10. {
    11. int val, x, y;
    12. }ve[N];
    13. int n, m, r, c, cnt;
    14. ll binpow(ll a, ll b)
    15. {
    16. ll res = 1;
    17. for(; b; b >>= 1)
    18. {
    19. if(b & 1) res = res * a % mod;
    20. a = a * a % mod;
    21. }
    22. return res;
    23. }
    24. ll inv(ll x) { return binpow(x, mod - 2); }
    25. bool cmp(node a, node b)
    26. {
    27. return a.val < b.val;
    28. }
    29. int main()
    30. {
    31. scanf("%d%d", &n, &m);
    32. _for(i, 1, n)
    33. _for(j, 1, m)
    34. {
    35. int x; scanf("%d", &x);
    36. ve[++cnt] = {x, i, j};
    37. }
    38. scanf("%d%d", &r, &c);
    39. sort(ve + 1, ve + cnt + 1, cmp);
    40. _for(i, 1, cnt)
    41. {
    42. x[i] = (x[i - 1] + ve[i].x) % mod; xx[i] = (xx[i - 1] + ve[i].x * ve[i].x) % mod;
    43. y[i] = (y[i - 1] + ve[i].y) % mod; yy[i] = (yy[i - 1] + ve[i].y * ve[i].y) % mod;
    44. }
    45. ll tot = 0;
    46. _for(i, 1, cnt)
    47. {
    48. if(i == 1 || ve[i].val != ve[i - 1].val) tot = i - 1;
    49. dp[i] = (tot * ve[i].x * ve[i].x % mod + xx[tot] - 2 * ve[i].x * x[tot] % mod +
    50. tot * ve[i].y * ve[i].y % mod + yy[tot] - 2 * ve[i].y * y[tot] % mod + f[tot]) % mod;
    51. dp[i] = dp[i] * inv(tot) % mod;
    52. f[i] = (f[i - 1] + dp[i]) % mod;
    53. if(ve[i].x == r && ve[i].y == c)
    54. {
    55. printf("%lld\n", dp[i]);
    56. break;
    57. }
    58. }
    59. return 0;
    60. }

    P1365 WJMZBMR打osu! / Easy(期望dp)

    这题比较考数学

    令Fi表示以i为结尾分数的期望。发现要用到以i-1为结尾的长度的期望,注意是它的期望。

    因此还要维护Li。

    1. #include<bits/stdc++.h>
    2. #define rep(i, a, b) for(int i = (a); i < (b); i++)
    3. #define _for(i, a, b) for(int i = (a); i <= (b); i++)
    4. using namespace std;
    5. const int N = 3e5 + 10;
    6. long double F[N], L[N];
    7. char s[N];
    8. int n;
    9. int main()
    10. {
    11. scanf("%d%s", &n, s + 1);
    12. _for(i, 1, n)
    13. {
    14. if(s[i] == 'o')
    15. {
    16. F[i] = F[i - 1] + 2 * L[i - 1] + 1;
    17. L[i] = L[i - 1] + 1;
    18. }
    19. else if(s[i] == 'x')
    20. {
    21. F[i] = F[i - 1];
    22. L[i] = 0;
    23. }
    24. else
    25. {
    26. F[i] = F[i - 1] + L[i - 1] + 0.5;
    27. L[i] = (L[i - 1] + 1) / 2;
    28. }
    29. }
    30. printf("%.4Lf\n", F[n]);
    31. return 0;
    32. }

    P1654 OSU!(期望dp)

    和上一题类似,只是平方改成了三次方,要维护的东西多了一些而已

    1. #include<bits/stdc++.h>
    2. #define rep(i, a, b) for(int i = (a); i < (b); i++)
    3. #define _for(i, a, b) for(int i = (a); i <= (b); i++)
    4. using namespace std;
    5. const int N = 1e5 + 10;
    6. double F[N], L[N], T[N], p[N];
    7. int n;
    8. int main()
    9. {
    10. scanf("%d", &n);
    11. _for(i, 1, n) scanf("%lf", &p[i]);
    12. _for(i, 1, n)
    13. {
    14. F[i] = (F[i - 1] + 3 * T[i - 1] + 3 * L[i - 1] + 1) * p[i] + F[i - 1] * (1 - p[i]);
    15. L[i] = (L[i - 1] + 1) * p[i];
    16. T[i] = (T[i - 1] + 2 * L[i - 1] + 1) * p[i];
    17. }
    18. printf("%.1f\n", F[n]);
    19. return 0;
    20. }

    P6046 纯粹容器(期望+组合数学)

    这题和dp没啥关系,很数学的一道题

    显然对于一个容器,答案是\sum f_i(i-1) 

    fi表示第i轮被干掉的概率 但是这个不好求,转化为前缀和

    f_i=p_i-p_{i-1}

    pi即前i轮被干掉的概率,这个好求,用古典概型求。

    分两种情况求,一种是被左边第一个大于它的干掉,一个是右边。

    把操作看作是在一条链上选择边,这样就有n-1条边去选,如果被左边的干掉,那么l~i的边一定要全选。

    1. #include<bits/stdc++.h>
    2. #define rep(i, a, b) for(int i = (a); i < (b); i++)
    3. #define _for(i, a, b) for(int i = (a); i <= (b); i++)
    4. using namespace std;
    5. typedef long long ll;
    6. const int N = 50 + 10;
    7. const int mod = 998244353;
    8. ll c[N][N], p[N];
    9. int n, a[N];
    10. ll binpow(ll a, ll b)
    11. {
    12. ll res = 1;
    13. for(; b; b >>= 1)
    14. {
    15. if(b & 1) res = res * a % mod;
    16. a = a * a % mod;
    17. }
    18. return res;
    19. }
    20. ll inv(ll x) { return binpow(x, mod - 2); }
    21. int main()
    22. {
    23. _for(i, 0, 50) c[i][0] = 1;
    24. _for(i, 1, 50)
    25. _for(j, 1, i)
    26. c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
    27. scanf("%d", &n);
    28. _for(i, 1, n) scanf("%d", &a[i]);
    29. _for(i, 1, n)
    30. {
    31. int l = -1, r = -1;
    32. for(int j = i - 1; j >= 1; j--)
    33. if(a[j] > a[i])
    34. {
    35. l = j;
    36. break;
    37. }
    38. _for(j, i + 1, n)
    39. if(a[j] > a[i])
    40. {
    41. r = j;
    42. break;
    43. }
    44. if(l == -1 && r == -1)
    45. {
    46. printf("%d ", n - 1);
    47. continue;
    48. }
    49. _for(j, 1, n - 1)
    50. {
    51. ll pa = 0, pb = 0, pab = 0;
    52. if(l != -1 && j >= (i - l)) pa = c[n - 1 - (i - l)][j - (i - l)] * inv(c[n - 1][j]) % mod;
    53. if(r != -1 && j >= (r - i)) pb = c[n - 1 - (r - i)][j - (r - i)] * inv(c[n - 1][j]) % mod;
    54. if(l != -1 && r != -1 && j >= (i - l) + (r - i)) pab = c[n - 1 - (r - i) - (i - l)][j - (r - i) - (i - l)] * inv(c[n - 1][j]) % mod;
    55. p[j] = (pa + pb - pab + mod) % mod;
    56. }
    57. ll ans = 0;
    58. _for(j, 1, n - 1)
    59. ans = (ans + (p[j] - p[j - 1]) * (j - 1)) % mod;
    60. printf("%lld ", (ans + mod) % mod);
    61. }
    62. return 0;
    63. }

    周四

    CF280C Game on Tree(期望)

    看作一个长度为n的操作序列

    操作看作染黑,一个点一染黑,就把它的子孙都染黑

    那么一个点被操作的前提是它的祖先都在它后面

    因此概率为 1 / dep[u]

    操作次数的期望等于每个点操作的期望之和

    每个点操作的期望就等于概率

    1. #include<bits/stdc++.h>
    2. #define rep(i, a, b) for(int i = (a); i < (b); i++)
    3. #define _for(i, a, b) for(int i = (a); i <= (b); i++)
    4. using namespace std;
    5. const int N = 1e5 + 10;
    6. vector<int> g[N];
    7. int d[N], n;
    8. void dfs(int u, int fa)
    9. {
    10. d[u] = d[fa] + 1;
    11. for(int v: g[u])
    12. {
    13. if(v == fa) continue;
    14. dfs(v, u);
    15. }
    16. }
    17. int main()
    18. {
    19. scanf("%d", &n);
    20. _for(i, 1, n - 1)
    21. {
    22. int u, v;
    23. scanf("%d%d", &u, &v);
    24. g[u].push_back(v);
    25. g[v].push_back(u);
    26. }
    27. dfs(1, 0);
    28. double ans = 0;
    29. _for(i, 1, n)
    30. ans += 1.0 / d[i];
    31. printf("%.10f\n", ans);
    32. return 0;
    33. }

    A. Little Pony and Expected Maximum(期望)

    和前面一道题的思路类似

    E=\sum f_i*i fi表示最大值为i的概率

    等于某一值的概率不好算,要转化成小于等于某一值的概率,这样就可以用古典概型

    f_i=p_i-p_{i-1}

    p_i=\left ( i/m \right )^n

    1. #include<bits/stdc++.h>
    2. #define rep(i, a, b) for(int i = (a); i < (b); i++)
    3. #define _for(i, a, b) for(int i = (a); i <= (b); i++)
    4. using namespace std;
    5. const int N = 1e5 + 10;
    6. long double p[N];
    7. int n, m;
    8. long double binpow(long double a, int b)
    9. {
    10. long double res = 1;
    11. for(; b; b >>= 1)
    12. {
    13. if(b & 1) res *= a;
    14. a *= a;
    15. }
    16. return res;
    17. }
    18. int main()
    19. {
    20. scanf("%d%d", &m, &n);
    21. _for(i, 1, m) p[i] = binpow(1.0 * i / m, n);
    22. long double ans = 0;
    23. _for(i, 1, m)
    24. ans += (p[i] - p[i - 1]) * i;
    25. printf("%.10Lf\n", ans);
    26. return 0;
    27. }

    SP1026 FAVDICE - Favorite Dice(期望)

    之前做过差不多一样的题,用dp做的。还有一种纯数学的方法

    如果现在有k个图案,拿到一个新的图案的概率为(n - k) / n

    有一个结论是,概率为p的事件发生所需要的期望次数是1/p

    因此此时要n / (n / k)期望次数才能发生

    这样枚举k,就可得到期望发生了多少次才得到全部答案

    周五

    P4550 收集邮票(期望)

    做这种期望题除了用期望dp,即设一个dp状态表示已经……还需要多少的期望。这题同样可以用这种思路。

    但还一种思路就是考虑每一次对答案的贡献,对于这道题这种思路特别简单

    如果已经有了k种,再增加一种的概率是(n - k) / n

    那么此时需要购买的期望次数为n / (n - k)

    那么枚举k从0到n-1,考虑每一次对答案的贡献

    对于次数就是当前的期望次数n / (n - k)

    对于价格,就是期望次数的前缀和,也就是当前是第几次购买

    1. #include<bits/stdc++.h>
    2. #define rep(i, a, b) for(int i = (a); i < (b); i++)
    3. #define _for(i, a, b) for(int i = (a); i <= (b); i++)
    4. using namespace std;
    5. const int N = 1e5 + 10;
    6. double p[N];
    7. int n;
    8. int main()
    9. {
    10. scanf("%d", &n);
    11. _for(i, 0, n - 1) p[i] = 1.0 * n / (n - i);
    12. double ans = 0, sum = 0;
    13. _for(i, 0, n - 1)
    14. {
    15. sum += p[i];
    16. ans += p[i] * sum;
    17. }
    18. printf("%.2f\n", ans);
    19. return 0;
    20. }

    CodeForces 547B(单调栈)

    考虑一个数的贡献,即当前数在多长的区间中可以作为最小值。这个可以用单调栈O(n)来实现。

    然后再把每个数的贡献合并起来即可。

    1. #include <bits/stdc++.h>
    2. #define rep(i, a, b) for(int i = (a); i < (b); i++)
    3. #define _for(i, a, b) for(int i = (a); i <= (b); i++)
    4. using namespace std;
    5. const int N = 2e5 + 10;
    6. int l[N], r[N], s[N], a[N], top, n;
    7. int t[N], ans[N];
    8. int main()
    9. {
    10. scanf("%d", &n);
    11. _for(i, 1, n) scanf("%d", &a[i]);
    12. for(int i = n; i >= 1; i--)
    13. {
    14. while(top && a[s[top]] >= a[i]) top--;
    15. r[i] = top ? s[top] : n + 1;
    16. s[++top] = i;
    17. }
    18. top = 0;
    19. _for(i, 1, n)
    20. {
    21. while(top && a[s[top]] >= a[i]) top--;
    22. l[i] = top ? s[top] : 0;
    23. s[++top] = i;
    24. }
    25. _for(i, 1, n)
    26. {
    27. int len = r[i] - l[i] - 1;
    28. t[len] = max(t[len], a[i]);
    29. }
    30. ans[n] = t[n];
    31. for(int i = n - 1; i >= 1; i--)
    32. ans[i] = max(ans[i + 1], t[i]);
    33. _for(i, 1, n) printf("%d ", ans[i]);
    34. return 0;
    35. }

    CodeForces 1540A(思维)

    关键是发现点的顺序是不影响的,所以直接排序,然后贪心即可

    1. #include <bits/stdc++.h>
    2. #define rep(i, a, b) for(int i = (a); i < (b); i++)
    3. #define _for(i, a, b) for(int i = (a); i <= (b); i++)
    4. using namespace std;
    5. typedef long long ll;
    6. const int N = 1e5 + 10;
    7. int a[N], n;
    8. int main()
    9. {
    10. int T; scanf("%d", &T);
    11. while(T--)
    12. {
    13. scanf("%d", &n);
    14. _for(i, 1, n) scanf("%d", &a[i]);
    15. sort(a + 1, a + n + 1);
    16. ll ans = 0, sum = 0;
    17. _for(i, 2, n) ans += a[i] - a[i - 1];
    18. sum = a[n];
    19. for(int i = n - 1; i >= 1; i--)
    20. {
    21. ans += 1LL * (n - i) * a[i] - sum;
    22. sum += a[i];
    23. }
    24. printf("%lld\n", ans);
    25. }
    26. return 0;
    27. }

    AtCoder - agc006_b (思维)

    中间放x-1 x x+1 剩下随便放

    思路很巧妙

    1. #include<bits/stdc++.h>
    2. #define REP(i, a, b) for(int i = (a); i < (b); i++)
    3. #define _for(i, a, b) for(int i = (a); i <= (b); i++)
    4. using namespace std;
    5. const int N = 2e5 + 10;
    6. int ans[N];
    7. int main()
    8. {
    9. int n, x;
    10. scanf("%d%d", &n, &x);
    11. if(x == 1 || x == 2 * n - 1)
    12. {
    13. puts("No");
    14. return 0;
    15. }
    16. ans[n - 1] = x - 1;
    17. ans[n] = x;
    18. ans[n + 1] = x + 1;
    19. int now = 0;
    20. _for(i, 1, 2 * n - 1)
    21. {
    22. if(ans[i]) continue;
    23. now++;
    24. if(now == x - 1) now = x + 2;
    25. ans[i] = now;
    26. }
    27. puts("Yes");
    28. _for(i, 1, 2 * n - 1) printf("%d\n", ans[i]);
    29. return 0;
    30. }

    周六

    hdu 2460(tarjan+lca)

    首先tarjan可以得到一颗生成树,割边一定是生成树上的边,因为这道题只考虑割边,所以得到割边后,非生成树上的边就不用考虑了。

    每次加边显然会把路径上的割边去掉,直接暴力的话计算一下是1e8,是可以过的,因此直接暴力就行。怎么保存割边呢,用k[u]表示u到u的父亲的这条边是否是割边

    1. #include<cstdio>
    2. #include<vector>
    3. #include<map>
    4. #include<algorithm>
    5. #define rep(i, a, b) for(int i = (a); i < (b); i++)
    6. #define _for(i, a, b) for(int i = (a); i <= (b); i++)
    7. using namespace std;
    8. const int N = 1e5 + 10;
    9. int dfn[N], low[N], k[N], d[N], f[N], cnt, n, m, sum;
    10. vector<int> g[N];
    11. void dfs(int u, int fa)
    12. {
    13. d[u] = d[fa] + 1;
    14. f[u] = fa;
    15. dfn[u] = low[u] = ++cnt;
    16. bool vis = false;
    17. for(int v: g[u])
    18. {
    19. if(!dfn[v])
    20. {
    21. dfs(v, u);
    22. low[u] = min(low[u], low[v]);
    23. if(low[v] > dfn[u]) k[v] = 1, sum++;
    24. }
    25. else
    26. {
    27. if(v == fa && !vis) vis = true;
    28. else low[u] = min(low[u], dfn[v]);
    29. }
    30. }
    31. }
    32. int main()
    33. {
    34. int kase = 0;
    35. while(scanf("%d%d", &n, &m))
    36. {
    37. if(n == 0 && m == 0) break;
    38. printf("Case %d:\n", ++kase);
    39. _for(i, 1, n) g[i].clear(), dfn[i] = low[i] = k[i] = f[i] = 0;
    40. cnt = sum = 0;
    41. while(m--)
    42. {
    43. int u, v;
    44. scanf("%d%d", &u, &v);
    45. g[u].push_back(v);
    46. g[v].push_back(u);
    47. }
    48. dfs(1, 0);
    49. // printf("sum: %d\n", sum);
    50. int q; scanf("%d", &q);
    51. while(q--)
    52. {
    53. int u, v;
    54. scanf("%d%d", &u, &v);
    55. if(d[u] < d[v]) swap(u, v);
    56. while(d[u] > d[v])
    57. {
    58. if(k[u]) k[u] = 0, sum--;
    59. u = f[u];
    60. }
    61. while(u != v)
    62. {
    63. if(k[u]) k[u] = 0, sum--;
    64. u = f[u];
    65. if(k[v]) k[v] = 0, sum--;
    66. v = f[v];
    67. }
    68. printf("%d\n", sum);
    69. }
    70. puts("");
    71. }
    72. return 0;
    73. }

    hdu 7158(同余)

    可以得到M是pq-1的因子,暴力枚举因子找到满足条件的即

    1. #include<bits/stdc++.h>
    2. #define rep(i, a, b) for(int i = (a); i < (b); i++)
    3. #define _for(i, a, b) for(int i = (a); i <= (b); i++)
    4. using namespace std;
    5. typedef long long ll;
    6. ll p, q, e;
    7. bool check(ll x)
    8. {
    9. if(x <= max(p, q)) return false;
    10. for(int i = 2; 1LL * i * i <= x; i++)
    11. if(x % i == 0)
    12. return false;
    13. return true;
    14. }
    15. int main()
    16. {
    17. int T; scanf("%d", &T);
    18. while(T--)
    19. {
    20. scanf("%lld%lld%lld", &p, &q, &e);
    21. ll t = p * q - 1, m = -1;
    22. for(int i = 1; 1LL * i * i <= t; i++)
    23. if(t % i == 0)
    24. {
    25. if(check(i))
    26. {
    27. m = i;
    28. break;
    29. }
    30. if(check(t / i))
    31. {
    32. m = t / i;
    33. break;
    34. }
    35. }
    36. if(m == -1) puts("shuanQ");
    37. else printf("%lld\n", e * q % m);
    38. }
    39. return 0;
    40. }

    周日

    hdu5222(拓扑排序+并查集)

    我一开始的思路不是正解的思路,是直接dfs,但是不知道为啥mle了,算一下空间是没超的,之前有一道hdu的题也是这样……

    正解是先考虑无向边,用并查集,如果没有成环的话,那么接下来就缩点,就是一个联通分量缩成一个点,然后图就是有向图了,那么由向图判断有无环就用拓扑排序即可

    1. #include<cstdio>
    2. #include<vector>
    3. #include<algorithm>
    4. #include<map>
    5. #include<queue>
    6. #define rep(i, a, b) for(int i = (a); i < (b); i++)
    7. #define _for(i, a, b) for(int i = (a); i <= (b); i++)
    8. using namespace std;
    9. #pragma comment(linker, "/STACK:102400000,102400000")
    10. const int N = 1e6 + 10;
    11. int f[N], d[N], n, m1, m2, ans;
    12. vector<int> g[N];
    13. int find(int x) { return x == f[x] ? x : f[x] = find(f[x]); }
    14. int main()
    15. {
    16. int T; scanf("%d", &T);
    17. while(T--)
    18. {
    19. scanf("%d%d%d", &n, &m1, &m2);
    20. _for(i, 1, n) f[i] = i, d[i] = 0, g[i].clear();
    21. ans = 0;
    22. while(m1--)
    23. {
    24. int u, v;
    25. scanf("%d%d", &u, &v);
    26. int fu = find(u), fv = find(v);
    27. if(fu == fv) ans = 1;
    28. else f[fu] = fv;
    29. }
    30. while(m2--)
    31. {
    32. int u, v;
    33. scanf("%d%d", &u, &v);
    34. u = find(u); v = find(v);
    35. g[u].push_back(v);
    36. d[v]++;
    37. }
    38. queue<int> q;
    39. vector<int> topo;
    40. _for(i, 1, n)
    41. if(!d[i])
    42. q.push(i);
    43. while(!q.empty())
    44. {
    45. int u = q.front(); q.pop();
    46. topo.push_back(u);
    47. for(int v: g[u])
    48. if(--d[v] == 0)
    49. q.push(v);
    50. }
    51. if(topo.size() != n) ans = 1;
    52. puts(ans ? "YES" : "NO");
    53. }
    54. return 0;
    55. }

    AtCoder - arc134_c(插板法)

    首先非1的球随便放,每种是独立的,它放就是n个球放到k个盒子中,盒子不相同,可以有空,这就是典型的插板法了,即C(n + k - 1, k - 1) 

    放完之后,先在每个盒子中,有多少球就放多少1,然后即剩下的1为n,那么就是将n个球放到k个盒子中,盒子不相同,每个至少有一个,即C(n - 1, k - 1)

    无解的话,组合数那里n

    关于组合数的计算,由于C(n, m)的m很小,所以可以直接暴力计算。

    1. #include<bits/stdc++.h>
    2. #define rep(i, a, b) for(int i = (a); i < (b); i++)
    3. #define _for(i, a, b) for(int i = (a); i <= (b); i++)
    4. using namespace std;
    5. typedef long long ll;
    6. const int mod = 998244353;
    7. const int N = 1e5 + 10;
    8. ll f[N], a[N];
    9. int n, k;
    10. ll binpow(ll a, ll b)
    11. {
    12. ll res = 1;
    13. for(; b; b >>= 1)
    14. {
    15. if(b & 1) res = res * a % mod;
    16. a = a * a % mod;
    17. }
    18. return res;
    19. }
    20. ll inv(ll x) { return binpow(x, mod - 2); }
    21. ll C(ll n, ll m)
    22. {
    23. if(n < m) return 0;
    24. ll res = 1;
    25. _for(i, n - m + 1, n) res = res * i % mod;
    26. return res * inv(f[m]) % mod;
    27. }
    28. int main()
    29. {
    30. f[0] = 1;
    31. _for(i, 1, 200) f[i] = f[i - 1] * i % mod;
    32. scanf("%d%d", &n, &k);
    33. _for(i, 1, n) scanf("%lld", &a[i]);
    34. ll ans = 1, sum = 0;
    35. _for(i, 2, n)
    36. {
    37. ans = ans * C(a[i] + k - 1, k - 1) % mod;
    38. sum += a[i];
    39. }
    40. ans = ans * C(a[1] - sum - 1, k - 1) % mod;
    41. printf("%lld\n", ans);
    42. return 0;
    43. }

    D. Round Subset(背包)

    这题的难点是想到把一个作为体积,一个作为价值跑背包

    还是做的题太少

    1. #include<bits/stdc++.h>
    2. #define rep(i, a, b) for(int i = (a); i < (b); i++)
    3. #define _for(i, a, b) for(int i = (a); i <= (b); i++)
    4. using namespace std;
    5. typedef long long ll;
    6. const int N = 200 + 10;
    7. int f[N * 30][N], w[N], v[N], sum, n, K;
    8. int main()
    9. {
    10. scanf("%d%d", &n, &K);
    11. _for(i, 1, n)
    12. {
    13. ll x; scanf("%lld", &x);
    14. while(x % 5 == 0)
    15. {
    16. x /= 5;
    17. w[i]++;
    18. }
    19. while(x % 2 == 0)
    20. {
    21. x /= 2;
    22. v[i]++;
    23. }
    24. sum += w[i];
    25. }
    26. //注意不合法的状态初始化为最小值
    27. memset(f, -0x3f, sizeof f);
    28. f[0][0] = 0;
    29. _for(i, 1, n)
    30. for(int j = sum; j >= w[i]; j--)
    31. for(int k = K; k >= 1; k--)
    32. f[j][k] = max(f[j][k], f[j - w[i]][k - 1] + v[i]);
    33. int ans = 0;
    34. _for(i, 0, sum)
    35. ans = max(ans, min(f[i][K], i));
    36. printf("%d\n", ans);
    37. return 0;
    38. }

  • 相关阅读:
    力扣(LeetCode)算法_C++——同构字符串
    如何系统 如何进行SQL监控-执行SQL分析打印
    面试(乐观锁和悲观锁)
    常用元器件使用方法35:SPI Flash芯片W25Q128JVSIQ
    一分钟学习数据安全—自主管理身份SSI可验证凭证
    vue的v-for中循环修改变量(this.xxx)的给子组件传值覆盖重复的问题
    IMU标定
    【学习笔记】CF708E Student‘s Camp
    刷题2个月,终于挺进梦寐以求的大厂,数据结构和算法太TM重要了
    损失函数(Loss Function)与代价函数(Cost Function)、目标函数(Objective Function)区别
  • 原文地址:https://blog.csdn.net/qq_34416123/article/details/126617351