Atcoder arc093F 容斥+DP

版权声明:本文是蒟蒻写的,转载。。。随便吧 https://blog.csdn.net/xgc_woker/article/details/86317691

Decription
2 n 2^n 个人,编号 1 , 2 , 3 1,2,3… 。他们摔 跤,过程如下:

  1. 选择一个 2 n 2^n 的排列;
  2. 所有人按排列的顺序乖乖♂站♂好;
  3. 按照站的顺序第 1 1 个人和第 2 2 个人直抽,第 3 3 个人和第 4 4 个人直抽,依次类推;
  4. 一轮摔♂跤结束后,剩余的人将保持相对顺序;
  5. 重复 3 , 4 3,4 直到剩一个人,他将是冠军。

判定两个人胜负的方法:
给定 m m 个不同的正整数,编号为 1 1 的人与这 m m 个人摔♂跤会输,其他会赢。对于 2 x < y 2 n 2≤x<y≤2^n x , y x,y 摔跤时 x x 会赢。
给定 n , m n,m ,以及 m m 个数,问有多少种初始排列使得编号为1的人是冠军。


Sample Input
2 1
3


Sample Output
8


首先这是个二叉树,求出答案乘个 2 n 2^n
考虑进行容斥,设第1个人至少输了k次,那么答案为:
k = 1 n ( 1 ) k F ( k ) \sum_{k=1}^n(-1)^kF(k)
f ( S ) f(S) ,为第一个人输掉的状态那么直接考虑DP即可。


#include <ctime>
#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <algorithm>

using namespace std;
typedef long long LL;
int _max(int x, int y) {return x > y ? x : y;}
int _min(int x, int y) {return x < y ? x : y;}
const LL mod = 1e9 + 7;
const int N = 17;
int read() {
	int s = 0, f = 1; char ch = getchar();
	while(ch < '0' || ch > '9') {if(ch == '-') f = -1; ch = getchar();}
	while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
	return s * f;
}
void put(int x) {
	if(x >= 10) put(x / 10);
	putchar(x % 10 + '0');
}

LL f[1 << N];
LL jc[1 << N], inv[1 << N];
int a[N], bin[N], cnt[1 << N];

LL pow_mod(LL a, int k) {
	LL ans = 1;
	while(k) {
		if(k & 1) (ans *= a) %= mod;
		(a *= a) %= mod; k /= 2;
	} return ans;
}

LL C(int n, int m) {
	if(n < m) return 0;
	return jc[n] * inv[m] % mod * inv[n - m] % mod;
}

int main() {
	int n = read(), m = read();
	for(int i = 1; i <= m; i++) a[i] = read();
	bin[0] = 1; for(int i = 1; i <= n; i++) bin[i] = bin[i - 1] * 2;
	jc[0] = inv[0] = 1; for(int i = 1; i <= bin[n]; i++) jc[i] = jc[i - 1] * i % mod;
	inv[bin[n]] = pow_mod(jc[bin[n]], mod - 2); for(int i = bin[n] - 1; i >= 1; i--) inv[i] = inv[i + 1] * (i + 1) % mod;
	cnt[0] = 0; for(int i = 1; i <= bin[n]; i++) cnt[i] = cnt[i >> 1] + (i & 1);
	f[0] = 1; sort(a + 1, a + m + 1);
	for(int i = m; i >= 1; i--) {
		for(int j = bin[n] - 1; j >= 0; j--) {
			for(int k = 0; k < n; k++) if(bin[k] & j){
				(f[j] += f[j ^ bin[k]] * C(bin[n] - a[i] - j + bin[k], bin[k] - 1) % mod * jc[bin[k]]) %= mod;
			}
		}
	} LL ans = 0;
	for(int i = 0; i < bin[n]; i++) {
		f[i] = f[i] * jc[bin[n] - i - 1] % mod;
		cnt[i] & 1 ? ans = (ans - f[i] + mod) % mod : ans = (ans + f[i]) % mod;
	} printf("%d\n", ans * bin[n] % mod);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/xgc_woker/article/details/86317691