逸一时,误一世!|

MistZero

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

CF961G Partitions Solution

这题好神啊 ... 我发现我一直 WA5 的原因是开始统计和的时候没有取模(
然后就想着写篇题解记录一下 ...

首先题目不是要求一个和吗。

那么通过人类智慧发现很显然所有数出现的次数都是一样的。
也就是说最后的和式必然是 ...×i=1nwi 这样的形式。

那么不妨将前面的系数设为 p

我们对于这个问题其实可以用 DP 的思想来解决。
我们对于求整个柿子的系数,直接考虑枚举子集大小。
然后就可以发现我们当前选了 i 个,然后算出自己集合的方案和别的集合的方案即可。
最后相乘累加就是答案,其中运用的是第二类斯特林数的思想。

第二类斯特林数就解决的是这样一类问题:

  • n 个物品放入 m 个不同集合中,求集合非空的总方案数。

想要研究的同学可以自行研究,这里不再赘述。

所以就可以得到系数 p 变成了下面的柿子。

p=i=1ni(n1i1)S2(ni,k1)

=i=1ni(n1i1)1(k1)!j=0k1(1)j(k1j)(kj1)ni

=i=1ni(n1i1)1(k1)!j=0k1(1)j(k1)!j!×(kj1)!(kj1)ni

=i=1ni(n1i1)j=0k1(1)j(kj1)nij!×(kj1)!

=j=0k1(1)jj!×(kj1)!i=1ni(n1i1)(kj1)ni

注意上面将 j 的和式提前了,这显然是正确的。
因为分母相同相加分母不变,然后 1 的系数显然只和 j 有关。
但是分子剩下的部分 (kj1)ni 显然和 ij 都有关。
所以为了方便后面的推导,这里直接将 j 提前作为后面那一堆和的系数。
这里说的 方便推导 其实也没有什么非常清晰的界定,只是这部分可以简单解决。
当然不排除有暴力老哥直接卷的情况。(
其实我也不知道可不可以卷,因为我没学多项式((

所以看到一大堆阶乘,在保证答案正确性的情况下,直接从一大堆柿子里面拎出来是必要的。

然后后面那个 (kj1) 看起来特别丑,弄成 s

i=1ni(n1i1)sni

=i=1ni×sni(n1)!(i1)!×(ni)!

为了约分上面的组合数拆开了。

然后有一个 trick,
看到分母有一个 i1,看到前面的系数 i
所以把那个 i 给拆掉,拆成 i=i1+1

所以有

=i=1nsni×(i1+1)(n1)!(i1)!×(ni)!

=i=1nsni[(i1)×(n1)!(i1)!×(ni)!+(n1)!(i1)!×(ni)!]

=i=1nsni(i1)(n1)!(i1)!×(ni)!+i=1nsni(n1)!(i1)!×(ni)!

然后约掉共同的 i1

=i=1nsni(n1)!(i2)!×(ni)!+i=1n(n1)!(i1)!×(ni)!

再重新写成方便的组合数形式。

=i=1nsni(n1i2)+i=1nsni(n1i1)

由于这个上下不统一所以把 (n1) 提出来。

=(n1)i=1nsni(n2i2)+i=1nsni(n1i1)

然后发现这个玩意儿好像很不好算,
所以变成下面这样,因为 (nm)=(nnm)

=(n1)i=1nsni(n2ni)+i=1nsni(n1ni)

然后根据那个什么柿子的展开,就是杨辉三角的系数,
观察到 ni 的共同部分可以运用上这个公式,
可以发现这个可以直接把组合数拆开变成幂。

实际上那个 i=1n 有一些项应该是不能算的,因为组合数其实等于 0
所以根据 (a+b)n 的系数其实就是组合数,可以直接代进去。

(a+b)n=i=0n(ni)anibi

应该长成这样吧,不太确定有没有问题。

所以又可以化简成下面那样。

=(n1)(s+1)n2+(s+1)n1

=(s+1)n2(s+1+n1)

=(s+1)n2(s+n)

然后就可以代回去了。

p=j=0k1(1)jj!×(kj1)![(n1)(kj)n2+(kj)n1]

=j=0k1(1)jj!×(kj1)!(kj)n2(n+kj1)

所以就可以得出代码了。
时间复杂度 Θ(klogMod)

#include <bits/stdc++.h>
#define int long long
using namespace std;

const int N = 2e5 + 10;
const int MOD = 1e9 + 7;
int n, k, cnt, sum, a, jc[N];

void init() {
  jc[0] = jc[1] = 1ll;
  for (int i=2; i<=k; ++i)
    jc[i] = jc[i-1] * i % MOD;
    // 预处理阶乘
  return ;
}

int qpow(int bas, int pw) {
  int mult = 1ll;
  while (pw) {
    if (pw & 1ll) mult = mult * bas % MOD;
    bas = bas * bas % MOD;
    pw /= 2;
    // 貌似是个 UB,不能写 >>=1ll
  } return mult;
}

int inv(int x) { return qpow(x, MOD-2); }

signed main() {
//   freopen("sum.in", "r", stdin);
  // freopen("sum.out", "w", stdout);
  scanf("%lld%lld", &n, &k); init();
  for (int i=1; i<=n; ++i)
    scanf("%lld", &a), cnt = (cnt + a) % MOD;
  int Sum = 0;
  for (int i=0; i<k; ++i) {
    int cur = inv(jc[i]);
    cur = cur * inv(jc[k-i-1]) % MOD;
    int f = i&1? -1 : 1;
    cur = f * cur % MOD, cur = (cur % MOD + MOD) % MOD;
    cur = cur * qpow(k-i, n-2) % MOD;
    cur = cur * (n+k-i-1) % MOD;
    Sum = (Sum + cur) % MOD;
    // 对于分母求逆元再乘分子,基本操作
  }
  cout << Sum * cnt % MOD << endl;
  return 0;
}

本文作者:MistZero

本文链接:https://www.cnblogs.com/MistZero/p/CF961G-Sol.html

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

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