我们先不考虑重排,最后除一个 m ! m! m! 就可以了
题意:值域在
[
1
,
n
]
[1,n]
[1,n] 间,选择
m
m
m 个不同的集合,使得每一个元素的出现次数为偶数
n
,
m
≤
1
e
6
n,m\le 1e6
n,m≤1e6
假设要选择
k
k
k 个集合,如果我们定下来
k
−
1
k-1
k−1 个,那么为了满足每一个元素都是偶数的限制,最后一个集合是确定的
大力
d
p
dp
dp,令
f
i
f_i
fi 表示选择
i
i
i 个集合且合法的方案数,容斥转移
需要减去前
k
−
1
k-1
k−1 个集合已经满足最后一个为空,以及最后一个钦定的与前面某一个集合相同的方案数
第一个就是
f
i
−
1
f_{i-1}
fi−1,第二个枚举重复的位置
注意集合间是有序的
f i = A 2 n − 1 i − 1 − f i − 1 − f i − 2 ∗ ( 2 n − 1 − ( i − 2 ) ) ∗ ( i − 1 ) f_i=A_{2^n-1}^{i-1}-f_{i-1}-f_{i-2}*(2^n-1-(i-2))*(i-1) fi=A2n−1i−1−fi−1−fi−2∗(2n−1−(i−2))∗(i−1)
#include<bits/stdc++.h>
#define cs const
using namespace std;
cs int Mod = 100000007;
cs int N = 1e6 + 5;
int add(int a, int b){ return a + b >= Mod ? a + b - Mod : a + b; }
int mul(int a, int b){ return 1ll * a * b % Mod; }
int ksm(int a, int b){ int ans = 1; for(;b;b>>=1,a=mul(a,a)) if(b&1) ans = mul(ans, a); return ans; }
int dec(int a, int b){ return a - b < 0 ? a - b + Mod : a - b; }
int n, m, fac[N], f[N];
int main(){
scanf("%d%d", &n, &m);
int up = dec(ksm(2, n),1); fac[0] = 1; int inv = 1;
for(int i = 1; i <= m; i++) inv = mul(inv, i); inv = ksm(inv, Mod-2);
for(int i = 1; i <= m; i++) fac[i] = mul(fac[i - 1], (up - i + 1));
f[0] = 1;
for(int i = 2; i <= m; i++){
f[i] = dec(fac[i - 1], add(f[i - 1], mul(mul(i-1, dec(up,i-2)), f[i-2])));
} cout << mul(f[m], inv); return 0;
}

本文探讨了一个关于集合选择的问题,利用组合数学与动态规划解决元素出现次数为偶数的约束条件下的方案计数问题。通过容斥原理进行状态转移,避免了重复计数的情况。
254

被折叠的 条评论
为什么被折叠?



