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;
}
posted @ 2021-01-26 10:59  feiko  阅读(110)  评论(0编辑  收藏  举报