逸一时,误一世!|

MistZero

园龄:5年5个月粉丝:8关注:3

数学做题笔记

P1829 [国家集训队] Crash 的数字表格:推式子。
本质上和 P3768 简单的数学题 没有多大区别,杜教筛筛一下 ϕ 就能解决了。

顺带一提,P3768 最后推出来是 j=1nS(nj)2(j2ϕ(j))。(

i=1nj=1mlcm(i,j)
=i=1nj=1mijgcd(i,j)
=d=1n1di=1nj=1m[gcd(i,j)=d] ij

这里是一如既往的换掉 lcm 变成 gcd
然后从 i,j 里面提出来 d2,显然不为 d 倍数时无贡献,那么变成

=d=1ndi=1ndj=1md[gcd(i,j)=1] ij

然后就可以开始莫反了,哈哈哈。
形式大约是 μ(i)g(i) 这样子推。

f(n,m)=i=1ndj=1md[gcd(i,j)=1] ijg(n)=d|nf(d)

就能得到

g(d)=i=1nj=1m[d|gcd(i,j)] ij

同理直接提 d2,得到

g(d)=d2i=1ndj=1mdij=d2(i=1ndi)(i=1mdi)

代回去得到 res=d=1ndi=1ndμ(i)i2(j=1nidj)(j=1midj)

可以了,O(n) 做法到手。下面看一眼 借鉴了 Solution 的 根号复杂度做法。

事实上这个做法的思路只是将枚举因数变成了枚举 id 也就是乘积
然后把前缀和给提出来预处理就变得非常迅速

=j=1n(i=1nji)(i=1mji)×ji|jiμ(i)

就可以愉快地 O(n) 单次询问求解啦。


P1446 [HNOI2008] Cards:大致是给出三种牌数然后给一堆置换,求方案数。

Burnside 引理板题。直接套上去即可。

这个洗牌就是一个置换群,用类似求环的方法直接求出来置换的循环即可。
求出来之后直接 DP 求解方案数即可~

inline int qinyubo() {
  int cnt = 0;
  for (int i = sr; ~i; --i) for (int j = sb; ~j; --j)
    for (int k = sg; ~k; --k) f[i][j][k] = 0;
  for (int i = 1; i <= n; ++i) vis[i] = false;
  for (int i = 1; i <= n; ++i) {
    if (vis[i]) continue;
    int cur = i, curans = 0;
    while (!vis[cur])
      ++curans, vis[cur] = true, cur = to[cur];
    siz[++cnt] = curans;
  }
  f[0][0][0] = 1;
  for (int cur = 1; cur <= cnt; ++cur)
    for (int i = sr; ~i; --i) for (int j = sb; ~j; --j)
      for (int k = sg; ~k; --k) {
        if (i >= siz[cur]) (f[i][j][k] += f[i-siz[cur]][j][k]) %= p;
        if (j >= siz[cur]) (f[i][j][k] += f[i][j-siz[cur]][k]) %= p;
        if (k >= siz[cur]) (f[i][j][k] += f[i][j][k-siz[cur]]) %= p;
      }
  return f[sr][sb][sg];
}

int main() {
  ios_base::sync_with_stdio(false);
  cin.tie(0), cout.tie(0);
  cin >> sr >> sb >> sg >> m >> p;
  n = sr + sb + sg;
  for (int i = 1; i <= m; ++i) {
    for (int j = 1; j <= n; ++j) cin >> to[j];
    (res += qinyubo()) %= p;
  }
  for (int i = 1; i <= n; ++i) to[i] = i;
  (res += qinyubo()) %= p;
  (res *= qpow(m + 1, p - 2)) %= p;
  cout << res << endl;
  return 0;
}

P3197 [HNOI2008] 越狱:哈哈哈哈哈哈哈哈哈哈哈哈题。

考虑不发生越狱的方案数,第一个房间有 m 种,后面都是 m1 种。
用总方案数减掉不合法数得出答案。

all = qpow(m, n), to = m * qpow(m - 1, n - 1) % md;
res = ((all - to) % md + md) % md;
cout << res << endl;

P4609 [FJOI2016] 建筑师:城市规划。

首先显然可以确定一个最高的建筑,以此为中心,分成左半和右半。
同时可以认为,左半分为了 A1 个段,右半为 B1
然后可以将一个可以被看见的建筑理解为一个以其为开头的段。
那么这一段中以其为开头,其余的可以随便排序。
计算答案发现,假设某一段长度为 x,则排列方案数为 Px1x1
而一个大小为 x 的环,排列方案数也恰好为 Pxxx=Px1x1

则可以理解为将 n1 个元素分为 A+B2 个环。

那么答案就是这样,左边右边分一下,一个组合数,然后分一下环,乘起来。

答案即为 S1(n1,A+B1)×(A+B2A1)

res 表示第一类斯特林数,c 表示组合数。

inline void init() {
  res[0][0] = 1;
  for (int j = 1; j <= 5e4; ++j)
    for (int i = 1; i <= 2e2; ++i)
      res[j][i] = (res[j-1][i-1] + (j - 1) * res[j-1][i]) % p;
  for (int i = 0; i <= 2e2; ++i) c[0][i] = 1;
  for (int i = 1; i <= 2e2; ++i)
    for (int j = 1; j <= 2e2; ++j)
      c[i][j] = (c[i-1][j-1] + c[i][j-1]) % p;
}

inline void query() {
  cin >> n >> a >> b;
  int ans = res[n-1][a+b-2] * c[a-1][a+b-2];
  cout << ans % p << endl;
}

CF932E Team Work:推式子题。

i=1n(ni)×ik
=i=1n(ni)j=0kS2(k,j)(n+1)j+1_j+1
=i=1nn!i! (ni)!j=0kS2(k,j)

本文作者:MistZero

本文链接:https://www.cnblogs.com/MistZero/p/Math-Notes-and-Problems.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   MistZero  阅读(86)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起