[关闭]
@computerkiller 2021-02-18T20:45:53.000000Z 字数 27170 阅读 476

数学题单种树计划

数学 题解


改了标题之后里面的题目就杂起来了

那么问题来了 计算几何算不算数学题

loj556 「Antileaf's Round」咱们去烧菜吧

套路题

考虑某个不为的物品的生成函数:


的物品生成函数:

最后的答案的生成函数:

给个代码:

  1. signed main()
  2. {
  3. read(n,m);
  4. for (int i = 1; i <= m; i++)
  5. {
  6. int a,b;
  7. read(a,b);
  8. if (a <= n) t[a] = (t[a] + 1) % mod;
  9. if (b && a * (b + 1) <= n) t[a * (b + 1)] = (t[a * (b + 1)] - 1 + mod) % mod;
  10. }
  11. for (int i = 1; i <= n; i++)
  12. for (int j = i; j <= n; j += i)
  13. f[j] = (f[j] + t[i] * ksm(j / i,mod - 2) % mod) % mod;
  14. getexp(f,g,n + 1);
  15. for (int i = 1; i <= n; i++)
  16. writeln(g[i]);
  17. return 0;
  18. }

loj6055 「from CommonAnts」一道数学题 加强版

神仙题 膜拜\color{black}\text{f}\color{red}\text{ffxk} 众所周知 qy稳了


然后拿这个算就好了qaq

草这题的k怎么是2000啊 还要爆踩标算才行

给那题加强一下好了 我们要求的是:


我们不妨求出递推式:

这里有个 在指数上 不能拉格朗日插值 我们考虑消除这个的影响 设出来一个函数:

考虑对 差分 方便地 我们定义 表示 阶差分:

注意到 的次数是 次 而 的次数是 次 降了一次幂

那么 也就是 阶差分是

所以 的通项是一个 次的多项式 我们需要 个点值来插值就可以得到 的通项 最后就可以得到

现在的问题转换成了如何求 这需要我们再次利用 阶差分是的特性:


必然是一个一次函数 所以可以通过上式解出 从而得到 通过拉格朗日插值 得到 所以就可以得到

复杂度是 我的实现下跑 是当前的最优解 然而这个最优解第二的 跑了(

给个最终的代码吧:

  1. template <typename T>
  2. void readb(T &a,T &b)
  3. {
  4. T x = 0,f = 1,y = 0;
  5. char ch = getchar();
  6. while (ch < '0' || ch > '9')
  7. {
  8. if (ch == '-') f = -1;
  9. ch = getchar();
  10. }
  11. while (ch >= '0' && ch <= '9')
  12. {
  13. x = ((x << 1) + (x << 3) + (ch ^ 48)) % mod;
  14. y = ((y << 1) + (y << 3) + (ch ^ 48)) % (mod - 1);
  15. ch = getchar();
  16. }
  17. a = x * f;
  18. b = y * f;
  19. }
  20. struct line
  21. {
  22. int k,t;
  23. line operator+(const line tmp) const
  24. {
  25. return (line) {(k + tmp.k) % mod,(t + tmp.t) % mod};
  26. }
  27. line operator-(const line tmp) const
  28. {
  29. return (line) {(k - tmp.k + mod) % mod,(t - tmp.t + mod) % mod};
  30. }
  31. line operator*(const int x) const
  32. {
  33. return (line) {k * x % mod,t * x % mod};
  34. }
  35. }l[N];
  36. int ksm(int a,int b,int mod = mod)
  37. {
  38. int res = 1;
  39. while (b)
  40. {
  41. if (b & 1) res = res * a % mod;
  42. a = a * a % mod;
  43. b >>= 1;
  44. }
  45. return res;
  46. }
  47. int C(int n,int m)
  48. {
  49. if (n < m) return 0;
  50. if (n < 0 || m < 0) return 0;
  51. return fac[n] * inv[n - m] % mod * inv[m] % mod;
  52. }
  53. int clac(int n)
  54. {
  55. if (n <= k + 1) return g[n];
  56. int ans = 0,res = 1;
  57. for (int i = 1; i <= k + 1; i++)
  58. res = res * (n - i) % mod;
  59. for (int i = 1; i <= k + 1; i++)
  60. {
  61. int tmp = inv[i - 1] * inv[k + 1 - i] % mod;
  62. ans = (ans + g[i] * tmp % mod * res % mod * ksm(n - i,mod - 2) % mod * ((k + 1 - i) & 1 ? -1 : 1) + mod) % mod;
  63. }
  64. return ans;
  65. }
  66. int getf(int n,int _n)
  67. {
  68. return (ksm(a,_n) * clac(n) % mod - g[0] + mod) % mod;
  69. }
  70. int getsum(int n,int _n)
  71. {
  72. int ans = getf(n,_n);
  73. return ans * ksm(2,_n) % mod;
  74. }
  75. signed main()
  76. {
  77. a = ksm(2,mod - 2);
  78. readb(n,_n);
  79. read(k);
  80. fac[0] = inv[0] = 1;
  81. for (int i = 1; i < N; i++)
  82. fac[i] = fac[i - 1] * i % mod;
  83. inv[N - 1] = ksm(fac[N - 1],mod - 2);
  84. for (int i = N - 1; i >= 1; i--)
  85. inv[i - 1] = inv[i] * i % mod;
  86. idk[1] = 1;
  87. for (int i = 2; i < N; i++)
  88. {
  89. if (!vis[i]) pri[++cnt] = i,idk[i] = ksm(i,k);
  90. for (int j = 1; j <= cnt && i * pri[j] < N; j++)
  91. {
  92. vis[i * pri[j]] = 1;
  93. idk[i * pri[j]] = idk[i] * idk[pri[j]] % mod;
  94. if (!(i % pri[j])) break;
  95. }
  96. }
  97. l[0] = (line) {1,0};
  98. for (int i = 1; i <= k + 1; i++)
  99. l[i] = l[i - 1] * 2 + (line) {0,idk[i] % mod};
  100. line res = (line) {0,0};
  101. for (int i = 0; i <= k + 1; i++)
  102. {
  103. if (i & 1) res = res - l[k + 1 - i] * C(k + 1,i);
  104. else res = res + l[k + 1 - i] * C(k + 1,i);
  105. }
  106. g[0] = (mod - res.t * ksm(res.k,mod - 2) % mod) % mod;
  107. for (int i = 1; i <= k + 5; i++)
  108. g[i] = (2 * g[i - 1] + idk[i]) % mod;
  109. writeln((getsum(n,_n) - getsum(n - 1,(_n - 1 + mod - 1) % (mod - 1)) + mod) % mod);
  110. return 0;
  111. }

loj6391 「THUPC2018」淘米神的树 / Tommy

8会

loj6609 无意识的石子堆 加强版

看着是可做题

首先考虑题意转换成为一个左边 个点右边 个点 条边的有标号二分图计数 满足所有点的度

而事实上 每行都会有 个棋子 所以:


是这个二分图的完美匹配数量:

给个实现:

  1. signed main()
  2. {
  3. read(n,m);
  4. fac[0] = inv[0] = 1;
  5. for (int i = 1; i <= n << 1; i++)
  6. fac[i] = fac[i - 1] * i % mod;
  7. inv[n << 1] = ksm(fac[n << 1],mod - 2);
  8. for (int i = n << 1; i >= 1; i--)
  9. inv[i - 1] = inv[i] * i % mod;
  10. for (int i = 0; i <= n; i++)
  11. f[i] = ksm(mod - 1,i) * fac[(n - i) << 1] % mod * C(n,i) % mod * ksm(2,i) % mod;
  12. for (int i = 0; i <= n; i++)
  13. g[i] = inv[i];
  14. lim = 1;
  15. while (lim <= n + n) lim <<= 1;
  16. getr(lim);
  17. ntt(f,1);
  18. ntt(g,1);
  19. for (int i = 0; i < lim; i++) f[i] = f[i] * g[i] % mod;
  20. ntt(f,-1);
  21. down[0] = 1;
  22. for (int i = 1; i <= n << 1; i++)
  23. down[i] = down[i - 1] * ((m - i + 1) % mod) % mod;
  24. int ans = 0;
  25. for (int i = 0; i <= n; i++)
  26. ans = (ans + down[n + n - i] * fac[i] % mod * ksm(ksm(2,n + i),mod - 2) % mod * f[i] % mod * inv[i] % mod * inv[(n - i) << 1] % mod) % mod;
  27. writeln(ans);
  28. return 0;
  29. }

loj6703 小 Q 的序列

考虑一个 表示到个数 之前选了个数的价值和 有转移:


考虑优化这个的式子 看着很像是斯特林数的递推式 我们考虑仿照斯特林数的做法来推一下这个式子的递推

尝试换一种写法表示前个数中有个不选的价值和:


阴间组合意义考虑个数放入个盒子中:

一个位置的数有种情况:不选() 或者加入一个集合() 或者新开一个集合()

一个数给你乘的贡献相当于这个位置上的数不选

我们不妨拆开贡献 将选个数不选的贡献先求出来 这个的贡献:


钦定了不选的数之后考虑放入的问题

我们设:表示个数中按照上述贡献分成个集合的价值

显然有 我们假设 那么

写出这个的EGF:


而我们的答案是:

所以考虑 的EGF 就是:

给个实现:

  1. void cdq(poly &f,int l,int r)
  2. {
  3. if (l == r)
  4. {
  5. f.resize(r - l + 2);
  6. f[0] = 1;
  7. f[1] = (a[l] + l) % mod;
  8. return ;
  9. }
  10. int mid = (l + r) >> 1;
  11. poly A,B;
  12. cdq(A,l,mid);
  13. cdq(B,mid + 1,r);
  14. lim = 1;
  15. while (lim <= (r - l + 1)) lim <<= 1;
  16. getr(lim);
  17. ntt(A,1);
  18. ntt(B,1);
  19. f.resize(lim);
  20. for (int i = 0; i < lim; i++) f[i] = A[i] * B[i] % mod;
  21. ntt(f,-1);
  22. f.resize(r - l + 2);
  23. }
  24. signed main()
  25. {
  26. read(n);
  27. for (int i = 1; i <= n; i++)
  28. read(a[i]);
  29. cdq(f,1,n);
  30. fac[0] = inv[0] = 1;
  31. for (int i = 1; i <= n; i++)
  32. fac[i] = fac[i - 1] * i % mod;
  33. inv[n] = ksm(fac[n],mod - 2);
  34. for (int i = n; i >= 1; i--)
  35. inv[i - 1] = inv[i] * i % mod;
  36. for (int i = 1; i <= n; i++)
  37. g[i] = mod - inv[i];
  38. getexp(g,h,n + 1);
  39. for (int i = 0; i <= n; i++)
  40. h[i] = h[i] * fac[i] % mod;
  41. int ans = 0;
  42. for (int i = 0; i <= n; i++)
  43. {
  44. int qaq = n - i;
  45. if (qaq & 1) ans = (ans - f[i] * h[qaq] % mod + mod) % mod;
  46. else ans = (ans + f[i] * h[qaq] % mod) % mod;
  47. }
  48. writeln(ans - 1);
  49. return 0;
  50. }

loj6247 九个太阳

高质量板子题(?)


给个实现好了

  1. signed main()
  2. {
  3. read(n,k);
  4. int w = ksm(3,(mod - 1) / k);
  5. int wn = 1,ans = 0;
  6. for (int i = 0; i < k; i++)
  7. {
  8. ans = (ans + ksm(wn + 1,n)) % mod;
  9. wn = wn * w % mod;
  10. }
  11. writeln(ans * ksm(k,mod - 2) % mod);
  12. return 0;
  13. }

AGC005F Many Easy Problems

套路地考虑对于点对于 的贡献 有这个式子:


把上式改写成:

翻转 之后用ntt加速即可 给个实现:

  1. void dfs(int u,int f)
  2. {
  3. siz[u] = 1;
  4. for (int i = head[u]; i != -1; i = nxt[i])
  5. {
  6. int v = pnt[i];
  7. if (v == f) continue;
  8. dfs(v,u);
  9. siz[u] += siz[v];
  10. cnt[siz[v]]++;
  11. }
  12. cnt[n - siz[u]]++;
  13. }
  14. signed main()
  15. {
  16. memset(head,-1,sizeof(head));
  17. memset(cnt,0,sizeof(cnt));
  18. read(n);
  19. for (int i = 1; i < n; i++)
  20. {
  21. int u,v;
  22. read(u,v);
  23. add_edge(u,v);
  24. add_edge(v,u);
  25. }
  26. inv[1] = 1;
  27. for (int i = 2; i <= n; i++)
  28. inv[i] = (mod - mod / i) * inv[mod % i] % mod;
  29. fac[0] = ifac[0] = 1;
  30. for (int i = 1; i <= n; i++)
  31. fac[i] = fac[i - 1] * i % mod,ifac[i] = ifac[i - 1] * inv[i] % mod;
  32. dfs(1,0);
  33. for (int i = 1; i <= n; i++)
  34. f[i] = cnt[i] * fac[i] % mod;
  35. reverse(f,f + n + 1);
  36. for (int i = 0; i <= n; i++)
  37. g[i] = ifac[i];
  38. lim = 1;
  39. while (lim <= (n << 1)) lim <<= 1;
  40. getr(lim);
  41. ntt(f,1);
  42. ntt(g,1);
  43. for (int i = 0; i < lim; i++)
  44. f[i] = f[i] * g[i] % mod;
  45. ntt(f,-1);
  46. reverse(f,f + n + 1);
  47. for (int i = 1; i <= n; i++)
  48. writeln((n * C(n,i) % mod - ifac[i] * f[i] % mod + mod) % mod);
  49. return 0;
  50. }

CF1096G Lucky Tickets

好像是个憨憨题诶

考虑计算出 位可以得到的数位的和以及对应的方案数 那么答案就是:


写出对应的生成函数:

那么最后我们要的生成函数就是

我竟然找到了多项式快速幂的应用(

然后我就头铁写ln+exp的卡了一下午常

没想到吧 exp能跑1e6!甚至不是最慢解

loj2541 「PKUWC2018」猎人杀

考虑容斥,不合法的答案是有一些数在1之后才被删除

定义全集,以及令

那么我们很容易得到这样一个不合法概率的式子:

而我们知道:

所以我们可以得到:

我们考虑套路地更换枚举顺序:

我们考虑后面地这个东西应该如何计算,不妨考虑他的生成函数:

对于某个数,表示不选这个数,而表示选这个数,那么我们所要的式子就是:


我们观察数据范围,发现(1)可以直接暴力乘法得到,所以我们只需要考虑(2)式.

(2)式的处理我们考虑一个分治,并不是分治fft,只是单纯地每次将其分成两部分,再将两部分合并

所以我们要的答案就是:


给个卡常之后的丑陋实现

CF438E The Child and Binary Tree

考虑如何计算和为 的方案数:


我们做以下定义:

我们有:

显然 符合题意 但是这个 的常数项是0 难以计算 所以我们需要分母无理化:

可以上板子直接算了

  1. signed main()
  2. {
  3. read(n,m);
  4. m++;
  5. for (int i = 1; i <= n; i++)
  6. {
  7. int x;
  8. read(x);
  9. if (x < m) g[x] = mod - 4;
  10. }
  11. g[0]++;
  12. getsqrt(g,h,m);
  13. h[0]++;
  14. getinv(h,f,m);
  15. for (int i = 1; i < m; i++)
  16. writeln(2 * f[i] % mod);
  17. return 0;
  18. }

ARC096C Everything on It

考虑一个容斥 我们设 表示至少有 个数出现次数小于等于 1 那么:


对于其他 个数没有限制 这部分的方案数是 而对于 的选择 方案数是

现在的问题是 个数出现次数小于等于 1 的方案数

我们把这个方案数设出来 设为

对于选出的一个元素 讨论 有两种情况:

  1. 不出现
  2. 仅出现一次

假设没有第一种情况 那方案数就是一行斯特林数的和 考虑转化问题 我们认为不出现的元素只是出现在了一个 假定的集合 中 此时我们添加一个标记元素 包含这个元素的集合就是假定集合

那么我们就可以计算 了:


把最后的答案写出来:

递推 Stirling 数即可

  1. signed main()
  2. {
  3. read(n,mod);
  4. inv[1] = 1;
  5. for (int i = 2; i <= n; i++)
  6. inv[i] = (mod - mod / i) * inv[mod % i] % mod;
  7. fac[0] = ifac[0] = 1;
  8. for (int i = 1; i <= n; i++)
  9. fac[i] = fac[i - 1] * i % mod,ifac[i] = ifac[i - 1] * inv[i] % mod;
  10. s[0][0] = 1;
  11. for (int i = 1; i <= n + 1; i++)
  12. for (int j = 1; j <= i; j++)
  13. s[i][j] = (s[i - 1][j - 1] + j * s[i - 1][j] % mod) % mod;
  14. int ans = 0;
  15. for (int i = 0; i <= n; i++)
  16. {
  17. int res = 0;
  18. for (int j = 0; j <= i; j++)
  19. res = (res + s[i + 1][j + 1] * ksm(2,j * (n - i)) % mod) % mod;
  20. res = res * C(n,i) % mod * ksm(2,ksm(2,n - i,mod - 1)) % mod;
  21. if (i & 1) ans = (ans - res + mod) % mod;
  22. else ans = (ans + res) % mod;
  23. }
  24. writeln(ans);
  25. return 0;
  26. }

CF923E Perpetual Subtraction

考虑一个 的 dp: 表示 轮前 数为 的概率

那么有转移:


快进到生成函数:

为了方便 我们设出一个生成函数 有:

所以我们可以得到:

即:

对应的 此时:

翻转后 ntt 加速求 我们便可求出 再求 考虑二项式反演:

将其中一个翻转后 同样 ntt 加速即可

总复杂度

  1. signed main()
  2. {
  3. read(n,m);
  4. m %= mod - 1;
  5. for (int i = 0; i <= n; i++)
  6. read(p[i]);
  7. inv[1] = 1;
  8. for (int i = 2; i <= n + 1; i++)
  9. inv[i] = (mod - mod / i) * inv[mod % i] % mod;
  10. fac[0] = ifac[0] = 1;
  11. for (int i = 1; i <= n + 1; i++)
  12. fac[i] = fac[i - 1] * i % mod,ifac[i] = ifac[i - 1] * inv[i] % mod;
  13. for (int i = 0; i <= n; i++)
  14. f[i] = ifac[i],g[i] = p[n - i] * fac[n - i] % mod;
  15. lim = 1;
  16. while (lim < (n << 1) + 1) lim <<= 1;
  17. getr(lim);
  18. ntt(f,1);
  19. ntt(g,1);
  20. for (int i = 0; i < lim; i++) g[i] = f[i] * g[i] % mod;
  21. ntt(g,-1);
  22. for (int i = 0; i <= n; i++)
  23. g[n - i] = ksm(inv[i + 1],m) * g[n - i] % mod;
  24. for (int i = 0; i <= n; i++)
  25. {
  26. if (i & 1) f[i] = mod - ifac[i];
  27. else f[i] = ifac[i];
  28. }
  29. for (int i = n + 1; i < lim; i++)
  30. f[i] = g[i] = 0;
  31. ntt(f,1);
  32. ntt(g,1);
  33. for (int i = 0; i < lim; i++) f[i] = f[i] * g[i] % mod;
  34. ntt(f,-1);
  35. reverse(f,f + n + 1);
  36. for (int i = 0; i <= n; i++)
  37. writes(f[i] * ifac[i] % mod);
  38. puts("");
  39. return 0;
  40. }

bzoj1815 [Shoi2006]color 有色图

经典题

根据 Polya 我们可以知道:


置换的总数显然是

考虑怎么求 事实上我们好处理的置换是点的置换而非边的置换 所以考虑拆分点的置换

把点的置换 拆分成 个的循环

边的两点分成两种:

  1. 在不同的循环中
  2. 在同一循环中

对于第一种情况 不妨考虑循环长度 对于循环 这之间连边的循环长度显然是 那么循环的数量 就是

考虑第二种情况 长度相等的边属于同一个等价类 所以有

考虑这样的拆分会对应多少个置换:


具体地考虑 把 放入 个至多放 的环中 显然有:

显然我们并不能去暴力枚举 我们应该把长度相等的压在一起计算

那么我们改写我们的最终的式子:


我们直接爆拆分拆数计算 注意最后我们还要除以 就是了

代码:

  1. void clac(int cnt)
  2. {
  3. int res = 0;
  4. for (int i = 1; i <= cnt; i++)
  5. {
  6. res = (res + a[i] / 2 * c[i] % (mod - 1)) % (mod - 1);
  7. res = (res + c[i] * (c[i] - 1) / 2 % (mod - 1) * a[i] % (mod - 1)) % (mod - 1);
  8. for (int j = 1; j < i; j++)
  9. res = (res + gcd(a[i],a[j]) * c[i] % (mod - 1) * c[j] % (mod - 1)) % (mod - 1);
  10. }
  11. res = ksm(m,res);
  12. for (int i = 1; i <= cnt; i++)
  13. res = res * ksm(inv[a[i]],c[i]) % mod * ifac[c[i]] % mod;
  14. ans = (ans + res) % mod;
  15. }
  16. void dfs(int n,int id,int mini)
  17. {
  18. if (!n) return clac(id - 1);
  19. for (int i = mini + 1; i <= n; i++)
  20. {
  21. a[id] = i;
  22. c[id] = 0;
  23. for (int j = i; j <= n; j += i)
  24. c[id]++,dfs(n - j,id + 1,i);
  25. }
  26. }
  27. signed main()
  28. {
  29. read(n,m,mod);
  30. inv[1] = 1;
  31. for (int i = 2; i < N; i++)
  32. inv[i] = (mod - mod / i) * inv[mod % i] % mod;
  33. fac[0] = ifac[0] = 1;
  34. for (int i = 1; i < N; i++)
  35. fac[i] = fac[i - 1] * i % mod,ifac[i] = ifac[i - 1] * inv[i] % mod;
  36. dfs(n,1,0);
  37. writeln(ans);
  38. return 0;
  39. }

CF755G PolandBall and Many Other Balls

考虑一个 dp:

表示前 个球取出 组的方案数 有转移:


我们把生成函数设出来 定义:

那么有:

快进到特征方程:

得到特征根:

其中:

套路地 设出通项公式为:


而我们知道的是:

我们可以带入解方程 得到:

  1. signed main()
  2. {
  3. read(n,k);
  4. k++;
  5. f[0] = 1,f[1] = 6,f[2] = 1;
  6. getsqrt(f,delta,k);
  7. for (int i = 0; i < k; i++)
  8. t1[i] = delta[i] * INV % mod,t2[i] = (mod - delta[i]) * INV % mod,f[i] = 0;
  9. t1[0] = (t1[0] + INV) % mod,t1[1] = (t1[1] + INV) % mod;
  10. t2[0] = (t2[0] + INV) % mod,t2[1] = (t2[1] + INV) % mod;
  11. getksm(t1,f,k,n + 1,n + 1,(int) (log(n + 1) / log(10)));
  12. getksm(t2,g,k,n + 1,n + 1,(int) (log(n + 1) / log(10)));
  13. getinv(delta,h,k);
  14. for (int i = 0; i < k; i++)
  15. f[i] = (f[i] + g[i]) % mod;
  16. lim = 1;
  17. while (lim < (k << 1) - 1) lim <<= 1;
  18. getr(lim);
  19. ntt(f,1);
  20. ntt(h,1);
  21. for (int i = 0; i < lim; i++) f[i] = f[i] * h[i] % mod;
  22. ntt(f,-1);
  23. for (int i = 1; i < k && i <= n; i++)
  24. writes(f[i]);
  25. for (int i = n + 1; i < k; i++)
  26. putchar('0'),putchar(' ');
  27. puts("");
  28. return 0;
  29. }

CF1295F Good Contest

确认过 是个套路题

首先有一个很显然的 dp : 表示前 个数 最后一个数是 的方案数

显然不行的是 值域很大 但是 很小 所以可以考虑离散化后对于一个左闭右开区间进行 dp

考虑 表示前 个数 最后那个数在 区间的方案数

转移时考虑枚举在 区间内的数的个数 然后统计答案

  1. signed main()
  2. {
  3. read(n);
  4. for (int i = 1; i <= n; i++)
  5. {
  6. read(l[i],r[i]);
  7. r[i]++;
  8. id[++cnt] = l[i];
  9. id[++cnt] = r[i];
  10. }
  11. sort(id + 1,id + cnt + 1);
  12. cnt = unique(id + 1,id + cnt + 1) - id - 1;
  13. for (int i = 1; i <= n; i++)
  14. {
  15. l[i] = lower_bound(id + 1,id + cnt + 1,l[i]) - id;
  16. r[i] = lower_bound(id + 1,id + cnt + 1,r[i]) - id;
  17. }
  18. for (int i = 1; i <= cnt; i++) dp[0][i] = 1;
  19. for (int i = 1; i <= n; i++)
  20. {
  21. for (int j = l[i]; j < r[i]; j++)
  22. {
  23. int len = id[j + 1] - id[j];
  24. for (int k = i; k >= 1; k--)
  25. {
  26. if (j < l[k] || r[k] <= j) break;
  27. dp[i][j] = (dp[i][j] + dp[k - 1][j + 1] * C(i - k + len,i - k + 1) % mod) % mod;
  28. }
  29. }
  30. for (int j = cnt - 1; j >= 1; j--)
  31. dp[i][j] = (dp[i][j] + dp[i][j + 1]) % mod;
  32. }
  33. for (int i = 1; i <= n; i++)
  34. dp[n][1] = dp[n][1] * ksm(id[r[i]] - id[l[i]],mod - 2) % mod;
  35. writeln(dp[n][1]);
  36. return 0;
  37. }

CF997C Sky Full of Stars

考虑: 表示 恰好 列同色的方案数 表示 至少 列同色的方案数

那么显然有:


考虑二项式反演:

而我们最终的答案是:

问题转化成了如何求 为了方便我们钦定同色的行列位置 但是 的时候要分讨

代回原来的式子:

我们需要化简的是中间那个式子 单独拿出来处理:

最后把式子写一遍:

复杂度 可以通过本题

  1. signed main()
  2. {
  3. read(n);
  4. inv[1] = 1;
  5. for (int i = 2; i <= n; i++)
  6. inv[i] = (mod - mod / i) * inv[mod % i] % mod;
  7. fac[0] = ifac[0] = 1;
  8. for (int i = 1; i <= n; i++)
  9. fac[i] = fac[i - 1] * i % mod,ifac[i] = ifac[i - 1] * inv[i] % mod;
  10. int ans = 0;
  11. for (int i = 1; i <= n; i++)
  12. {
  13. if (i & 1) ans = (ans - C(n,i) * ksm(ksm(3,(i * n) % (mod - 1)),mod - 2) % mod * (ksm(1 - ksm(ksm(3,n - i),mod - 2) + mod,n) - 1) % mod + mod) % mod;
  14. else ans = (ans + C(n,i) * ksm(ksm(3,(i * n) % (mod - 1)),mod - 2) % mod * (ksm(1 - ksm(ksm(3,n - i),mod - 2) + mod,n) - 1) % mod) % mod;
  15. }
  16. ans = ans * ksm(3,(n * n + 1) % (mod - 1));
  17. for (int i = 1; i <= n; i++)
  18. {
  19. if (i & 1) ans = (ans - 2 * C(n,i) * ksm(3,(n * (n - i) + i) % (mod - 1)) + mod) % mod;
  20. else ans = (ans + 2 * C(n,i) * ksm(3,(n * (n - i) + i) % (mod - 1))) % mod;
  21. }
  22. writeln((mod - ans) % mod);
  23. return 0;
  24. }

uoj419 【集训队作业2018】圆形

首先考虑圆的面积并怎么做 考虑格林公式:


区域 的面积是:

考虑设 则可以得到上式 现在考虑三角代换 设:

然后动态维护圆弧 求和

  1. const long double eps = 1e-11,pi = acos(-1.0);
  2. int n,vis[N];
  3. long double sqr(long double x)
  4. {
  5. return x * x;
  6. }
  7. struct point
  8. {
  9. long double x,y;
  10. bool operator<(const point &tmp) const
  11. {
  12. if (x != tmp.x) return x + eps < tmp.x;
  13. return y + eps < tmp.y;
  14. }
  15. bool operator==(const point &tmp) const
  16. {
  17. return fabs(x - tmp.x) <= eps && fabs(y - tmp.y) <= eps;
  18. }
  19. bool operator!=(const point &tmp) const
  20. {
  21. return !(fabs(x - tmp.x) <= eps && fabs(y - tmp.y) <= eps);
  22. }
  23. point operator+(const point &tmp) const
  24. {
  25. return (point) {x + tmp.x,y + tmp.y};
  26. }
  27. point operator-(const point &tmp) const
  28. {
  29. return (point) {x - tmp.x,y - tmp.y};
  30. }
  31. long double angle()
  32. {
  33. return atan2(y,x);
  34. }
  35. };
  36. struct cir
  37. {
  38. point o;
  39. long double r;
  40. bool operator<(const cir &tmp) const
  41. {
  42. if (o != tmp.o) return o < tmp.o;
  43. return r < tmp.r;
  44. }
  45. bool operator==(const cir &tmp) const
  46. {
  47. return o == tmp.o && fabs(r - tmp.r) <= eps;
  48. }
  49. long double oint(long double phi2,long double phi1)
  50. {
  51. return (r * (phi1 - phi2) + o.x * (sin(phi1) - sin(phi2)) - o.y * (cos(phi1) - cos(phi2))) * r;
  52. }
  53. }a[N];
  54. struct node
  55. {
  56. long double l,r;
  57. bool operator<(const node &tmp) const
  58. {
  59. return r + eps < tmp.r;
  60. }
  61. };
  62. set<node> s[N];
  63. bool in(cir x,cir y)
  64. {
  65. point tmp = x.o - y.o;
  66. return sqr(tmp.x) + sqr(tmp.y) <= sqr(y.r - x.r);
  67. }
  68. bool out(cir x,cir y)
  69. {
  70. point tmp = x.o - y.o;
  71. return sqr(tmp.x) + sqr(tmp.y) >= sqr(x.r + y.r);
  72. }
  73. long double update(int id,long double l,long double r)
  74. {
  75. long double res = 0;
  76. set<node>::iterator it2;
  77. for (set<node>::iterator it = s[id].lower_bound((node) {0,l}); it != s[id].end() && it -> l < r; it = it2)
  78. {
  79. long double nowl = it -> l,nowr = it -> r;
  80. it2 = it;
  81. it2++;
  82. s[id].erase(it);
  83. res -= a[id].oint(nowl,nowr);
  84. if (nowl < l) res += a[id].oint(nowl,l),s[id].insert((node) {nowl,l});
  85. if (nowr > r) res += a[id].oint(r,nowr),s[id].insert((node) {r,nowr});
  86. }
  87. return res;
  88. }
  89. signed main()
  90. {
  91. read(n,n);
  92. for (int i = 1; i <= n; i++)
  93. scanf("%Lf%Lf%Lf",&a[i].o.x,&a[i].o.y,&a[i].r);
  94. long double ans = 0;
  95. for (int i = 1; i <= n; i++)
  96. {
  97. vis[i] = 1;
  98. for (int j = 1; j < i; j++)
  99. {
  100. if (in(a[i],a[j]) && a[j].r >= a[i].r)
  101. {
  102. vis[i] = 0;
  103. break;
  104. }
  105. if (in(a[j],a[i]) && a[i].r >= a[j].r)
  106. {
  107. vis[j] = 0;
  108. ans += update(j,-pi,pi);
  109. }
  110. }
  111. if (!vis[i])
  112. {
  113. printf("%.10Lf\n",ans * 0.5);
  114. continue;
  115. }
  116. ans += a[i].oint(-pi,pi);
  117. s[i].insert((node) {-pi,pi});
  118. for (int j = 1; j < i; j++)
  119. {
  120. if (!vis[j] || out(a[i],a[j])) continue;
  121. point qaq = a[i].o - a[j].o;
  122. long double dis = sqrt(sqr(qaq.x) + sqr(qaq.y)),alpha = qaq.angle();
  123. long double beta = acos((sqr(dis) + sqr(a[j].r) - sqr(a[i].r)) / (2.0 * dis * a[j].r));
  124. long double l = alpha - beta,r = alpha + beta;
  125. if (l < -pi) l += 2.0 * pi;
  126. if (l > pi) l -= 2.0 * pi;
  127. if (r < -pi) r += 2.0 * pi;
  128. if (r > pi) r -= 2.0 * pi;
  129. if (l < r) ans += update(j,l,r);
  130. else ans += update(j,-pi,r),ans += update(j,l,pi);
  131. qaq = a[j].o - a[i].o;
  132. alpha = qaq.angle();
  133. beta = acos((sqr(dis) + sqr(a[i].r) - sqr(a[j].r)) / (2.0 * dis * a[i].r));
  134. l = alpha - beta,r = alpha + beta;
  135. if (l < -pi) l += 2.0 * pi;
  136. if (l > pi) l -= 2.0 * pi;
  137. if (r < -pi) r += 2.0 * pi;
  138. if (r > pi) r -= 2.0 * pi;
  139. if (l < r) ans += update(i,l,r);
  140. else ans += update(i,-pi,r),ans += update(i,l,pi);
  141. }
  142. printf("%.10Lf\n",ans * 0.5);
  143. }
  144. return 0;
  145. }

CF848E Days of Floral Colours

你怎么写个题面还写同位语从句的啊

首先套路地破环为链 考虑序列上 那么染色的方式可以大致分成四种考虑:

  1. 即两端同色 中间那个贺对面同色
  2. 即两两相邻同色
  3. 相邻
  4. 和对面同色

考虑 dp:

把上面那堆全部写成生成函数的形式:


我们跳过解方程 快进到结论:

然后我们如何求 我们有:

然后把 带入 得到:

带入 然后写出最后答案的生成函数 就能得到最后的答案是这个:

然后分母线性递推就可以了 :)

于是我就偷懒直接 ntt 了

  1. int f[N << 2] = {0,0,0,24,4,144,mod - 4,348,mod - 128,240,28,188,mod - 68,16,0,4,0};
  2. int g[N << 2] = {1,0,mod - 4,mod - 8,1,mod - 16,10,mod - 4,12,48,mod - 26,44,mod - 15,16,4,4,1};
  3. signed main()
  4. {
  5. read(n);
  6. n++;
  7. getinv(g,h,n);
  8. lim = 1;
  9. while (lim < (n << 1)) lim <<= 1;
  10. getr(lim);
  11. for (int i = n; i < lim; i++)
  12. f[i] = h[i] = 0;
  13. ntt(h,1);
  14. ntt(f,1);
  15. for (int i = 0; i < lim; i++) h[i] = h[i] * f[i] % mod;
  16. ntt(h,-1);
  17. writeln(h[n - 1]);
  18. return 0;
  19. }

loj2267 「SDOI2017」龙与地下城

u1s1 whk 好玩

题意写的太复杂了 简单来说就是:

给你一个随机数生成器 等概率地生成一个 的整数

你生成 次 问你生成的数的和在 的概率

我们知道一个满足正态分布的随机变量 的概率密度函数:


其中 是期望 是标准差

但是题目中的并不是正态分布 由中心极限定理可以知道:


足够大时可以近似看作正态分布

于是我们事实上就是求


用自带函数 求就可以了

小数据大力 就可以了

  1. namespace sub1
  2. {
  3. double f[N];
  4. signed main(int x,int y)
  5. {
  6. f[0] = pow(1.0 / x,y);
  7. for (int i = 1; i <= x * y - 1; i++)
  8. {
  9. f[i] = 0;
  10. for (int j = 1; j <= min(x - 1,i); j++)
  11. f[i] += y * j * f[i - j] - (i - j) * f[i - j];
  12. f[i] /= i;
  13. }
  14. for (int i = 1; i <= x * y - 1; i++)
  15. f[i] += f[i - 1];
  16. for (int t = 1; t <= 10; t++)
  17. {
  18. int l,r;
  19. read(l,r);
  20. printf("%.7lf\n",f[r] - (l ? f[l - 1] : 0));
  21. }
  22. return 0;
  23. }
  24. }
  25. namespace sub2
  26. {
  27. const double qaq = sqrt(2);
  28. signed main(int x,int y)
  29. {
  30. double mu = (x - 1) / 2.0 * y,sig = sqrt((x * x - 1) / 12.0 * y);
  31. for (int t = 1; t <= 10; t++)
  32. {
  33. int l,r;
  34. read(l,r);
  35. printf("%.7lf\n",(erf((r - mu) / sig / qaq) - erf((l - 1 - mu) / sig / qaq)) / 2);
  36. }
  37. return 0;
  38. }
  39. }
  40. signed main()
  41. {
  42. int t;
  43. read(t);
  44. while (t--)
  45. {
  46. int x,y;
  47. read(x,y);
  48. if (x * y <= 2000) sub1::main(x,y);
  49. else sub2::main(x,y);
  50. }
  51. return 0;
  52. }

AGC035F Two Histograms

考虑什么时候会重复计算方案 当且仅当

我们考虑去掉这种方案 我们统计不存在 的方案

考虑容斥或者二项式反演 枚举满足上条件的行列数 快进到最后的式子:


快进到代码:

  1. signed main()
  2. {
  3. inv[1] = 1;
  4. for (int i = 2; i < N; i++)
  5. inv[i] = (mod - mod / i) * inv[mod % i] % mod;
  6. fac[0] = ifac[0] = 1;
  7. for (int i = 1; i < N; i++)
  8. fac[i] = fac[i - 1] * i % mod,ifac[i] = ifac[i - 1] * inv[i] % mod;
  9. read(n,m);
  10. int ans = 0,res = 0;
  11. for (int i = 0; i <= n && i <= m; i++)
  12. {
  13. if (i & 1) ans = (ans - C(n,i) * C(m,i) % mod * fac[i] % mod * ksm(n + 1, m - i) % mod * ksm(m + 1,n - i) % mod + mod) % mod;
  14. else ans = (ans + C(n,i) * C(m,i) % mod * fac[i] % mod * ksm(n + 1, m - i) % mod * ksm(m + 1,n - i) % mod) % mod;
  15. }
  16. writeln(ans);
  17. return 0;
  18. }

ARC101C Ribbons on Tree

直接考虑容斥 枚举没有被染色的边的数量 考虑把边断开之后的每一棵子树分开染色 一棵大小为 的树的方案:


直接树形 dp 就可以了 时间复杂度

给个实现:

  1. void dfs(int u,int fa)
  2. {
  3. siz[u] = dp[u][1] = 1;
  4. for (int i = head[u]; i != -1; i = nxt[i])
  5. {
  6. int v = pnt[i];
  7. if (v == fa) continue;
  8. dfs(v,u);
  9. for (int j = 1; j <= siz[u] + siz[v]; j++) g[j] = 0;
  10. for (int j = siz[u]; j >= 1; j--)
  11. for (int k = siz[v]; k >= 0; k--)
  12. g[j + k] = (g[j + k] + dp[u][j] * dp[v][k] % mod) % mod;
  13. for (int j = 1; j <= siz[u] + siz[v]; j++) dp[u][j] = g[j];
  14. siz[u] += siz[v];
  15. }
  16. for (int i = 2; i <= siz[u]; i += 2)
  17. dp[u][0] = (dp[u][0] - dp[u][i] * f[i] % mod + mod) % mod;
  18. }
  19. signed main()
  20. {
  21. memset(head,-1,sizeof(head));
  22. read(n);
  23. for (int i = 1; i < n; i++)
  24. {
  25. int u,v;
  26. read(u,v);
  27. add_edge(u,v);
  28. add_edge(v,u);
  29. }
  30. f[0] = 1;
  31. for (int i = 2; i <= n; i += 2)
  32. f[i] = f[i - 2] * (i - 1) % mod;
  33. dfs(1,0);
  34. writeln(mod - dp[1][0]);
  35. return 0;
  36. }
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注