题解 CF1747E 【List Generation】
Update on 2022.11.9:感谢 @ClHg2。
前置芝士:二项式反演
考虑不合法序列的那个条件
于是我们可以得到合法序列长度
然后发现每存在一个位置不合法,我们就可以把不合法的位置删掉——反正重复了,删了也不影响剩下的部分。当我们把所有不合法位置删完后,你会发现它变成了一个合法序列。
设
运用二项式反演,由
现在考虑
原式
现在唯一的问题是后半部分。我们考虑按照
我们显然要根据
从
但是对于
时间复杂度为
代码(要贺的话记得用 C++20 提交):
#include <stdio.h>
const int N = 1.5e7, mod = 1e9 + 7;
int fac[N + 7], inv_fac[N + 7];
inline int quick_pow(int x, int p){
int ans = 1;
while (p){
if (p & 1) ans = 1ll * ans * x % mod;
x = 1ll * x * x % mod;
p >>= 1;
}
return ans;
}
inline void init(){
fac[0] = 1;
for (register int i = 1; i <= N; i++){
fac[i] = 1ll * fac[i - 1] * i % mod;
}
inv_fac[N] = quick_pow(fac[N], mod - 2);
for (register int i = N - 1; i >= 0; i--){
inv_fac[i] = 1ll * inv_fac[i + 1] * (i + 1) % mod;
}
}
inline int sub1(int x, int y){
return x - y < 0 ? x - y + mod : x - y;
}
inline int comb(int n, int m){
if (n < 0 || m < 0 || n < m) return 0;
return 1ll * fac[n] * inv_fac[m] % mod * inv_fac[n - m] % mod;
}
inline void add(int &x, int y){
if ((x += y) >= mod) x -= mod;
}
inline void sub2(int &x, int y){
if ((x -= y) < 0) x += mod;
}
int main(){
int t;
scanf("%d", &t);
init();
for (register int i = 1; i <= t; i++){
int n, m, even = 0, odd = 0, t, ans = 0;
scanf("%d %d", &n, &m);
t = n + m + 1;
for (register int j = t; j >= 2; j--){
int x = sub1(odd, even), y = mod - x;
if (t % 2 == 0){
add(x, comb(t + 1, j + 1));
} else {
add(y, comb(t + 1, j + 1));
}
even = x;
odd = y;
if (j % 2 == 0){
add(ans, 1ll * j * sub1(even, odd) % mod * comb(n + j - 2, n) % mod * comb(m + j - 2, m) % mod);
} else {
sub2(ans, 1ll * j * sub1(even, odd) % mod * comb(n + j - 2, n) % mod * comb(m + j - 2, m) % mod);
}
}
printf("%d\n", ans);
}
return 0;
}