BZOJ 2339 卡农

BZOJ 2339

题源:http://61.187.179.132:16386/JudgeOnline/problem.php?id=2339

耍猴神题,不太会。

有三个限制:1.非空 2.不同 3.出现的元素出现次数均为偶数

\(f_i\)为搞出\(i\)个集合的方案数,考虑怎么去掉这三个不合法的方案

1:搞掉第三个限制,任意钦点了前\(i-1\)个非空集合以后一定能构造出第\(i\)个集合,因此当前合法方案数为\((2^{n}-1)^{\underline {i-1}}\)

2:搞掉第一个限制,考虑\(i-1\)的合法方案构成的前\(i-1\)个集合,第\(i\)个集合只有空的时候才是合法的,因此不满足条件的正好是\(f_{i-1}\)

3:好了现在只剩下不同了,假设你搞出的第\(i\)个集合与之前的某个集合相同,显然这个时候是不可以的,因为这两个集合相同,所以剩下的\(i-2\)个集合一定是合法的,考虑相同情况的数量,因为当前是有序的,所以直接钦点和第\(j\)个集合相同,\(j\in[1,i-1]\),但是又不能和之前的\(i-2\)个集合相同,所以有\(2^n-1-(i-2)\)

转移就是\(f_i=(2^{n}-1)^{\underline {i-1}}-f_{i-1}-(i-1)\times(2^n-i+1)\times f_{i-2}\)

因为无序,所以最后除一个\(m!\)

#include<cstdio>
#include<cstring>
const int N = 2e6+7;
typedef long long LL;
#define R register
#define cerr(x) printf("%lld\n", x)
inline int iep(int a, int b) {
  return ((a - b) & 1) ? -1 : 1; 
}
const int p = 1e8+7;
inline LL FST(LL x, LL k) {
  LL res = 1;
  if (!k) return 1;
  if (!x) return 0;
  while (k) {
    if (k & 1LL) res = res * x % p;
    x = x * x % p, k >>= 1LL;
  } return res % p;
}
LL f[N], pw2[N], A[N], fac[N];
int main() {
  int n, m;
  scanf("%d%d", &n, &m);
  pw2[0] = A[0] = fac[0] = 1;
  for (R int i = 1; i <= 1e6; i++)
    pw2[i] = pw2[i - 1] * 2LL % p;
  for (R int i = 1; i <= 1e6; i++)
    A[i] = (A[i - 1] * (pw2[n] - i) % p + p) % p;
  f[f[0] = 1] = 0;
  for (R int i = 2; i <= m; i++)
    f[i] = ((A[i - 1] - f[i - 1] - (i - 1) * (pw2[n] - i + 1) % p * f[i - 2] % p) % p + p) % p;
  for (R int i = 1; i <= 1e6; i++)
    fac[i] = fac[i - 1] * i % p;
  printf("%lld\n", f[m] * FST(fac[m], p - 2) % p);
return 0;
}
posted @ 2019-10-17 09:22  ComeIntoCalm  阅读(...)  评论(...编辑  收藏