CodeForces 623E Transforming Sequence
定义一个非负整数序列 \(\{a_1,a_2,\ldots,a_n\}\) 的变换是 \(\{b_1,b_2,\ldots,b_n\}\),其中 \(b_i=a_1|a_2|\ldots|a_i\)。
求所有满足其变换 \(b\) 单调递增,且长度为 \(n\),所有数在 \([1,2^k-1]\) 范围内的 \(a\) 的个数。
定义 \(dp_{l,i}\) 表示长度为 \(l\),且其变换的第 \(l\) 个数 \(b_l\) 中含有 \(i\) 个 1 ,且所有数在 \([1,2^l-1]\) 的 \(a\) 的个数。不难写出转移方程
\[dp_{a+b,i}=\sum\limits_{j=1}^{i-1}2^{jb}\binom{i}{i-j}dp_{a,j}\cdot dp_{b,i-j}
\]
组合数可以转化为指数型生成函数的系数,而 \(2^{ib}\) 中的 \(b\) 在每次转移中可以视作常数,于是这个式子就是喜闻乐见的卷积形式了。
倍增转移即可,复杂度为 \(O(k\log^2k)\)(之所以不是 \(O(k\log k\log n)\),是因为 \(n>k\) 时答案恒为 \(0\))。由于出题人脑子有问题,模数非 FNTT 模数,我们需要任意模数多项式乘法。
#include <cstdio>
typedef long long ll;
const int maxn = 3e4 + 19, mod = 1e9 + 7;
int qpow(int a, int b){
int res = 1;
while(b){if(b & 1) res = (ll)res * a % mod; a = (ll)a * a % mod, b >>= 1; }
return res;
}
ll n;
int k, fact[maxn], ifact[maxn], shift[maxn], ans;
int binom(int n, int m){
return (ll)fact[n] * ifact[n - m] % mod * ifact[m] % mod;
}
int main(){
scanf("%lld%d", &n, &k);
if(n > k){puts("0"); return 0; }
fact[0] = 1;
for(int i = 1; i <= k; ++i) fact[i] = (ll)fact[i - 1] * i % mod;
ifact[k] = qpow(fact[k], mod - 2);
for(int i = k - 1; i >= 0; --i) ifact[i] = (ll)ifact[i + 1] * (i + 1) % mod;
shift[0] = 1;
for(int i = 1; i <= k; ++i) shift[i] = shift[i - 1] * 2 % mod;
fstdlib::m_poly f(k + 1, mod), g(k + 1, mod), h(k + 1, mod);
for(int i = 1; i <= k; ++i) f[i] = (ll)ifact[i] % mod;
for(int i = 1; i <= k; ++i) g[i] = f[i];
int b = n - 1, c = 1;
while(b){
if(b & 1){
for(int i = 0; i < (int)f.size(); ++i) f[i] = (ll)f[i] * qpow(2, i * c) % mod;
f = f * g, f.resize(k + 1);
}
for(int i = 0; i < (int)g.size(); ++i) h[i] = (ll)g[i] * qpow(2, i * c) % mod;
b >>= 1, c <<= 1, g *= h, g.resize(k + 1);
}
for(int i = n; i <= k; ++i) ans = (ans + (ll)f[i] * fact[i] % mod * binom(k, i)) % mod;
printf("%d\n", ans);
return 0;
}