luoguP7468 [NOI Online 2021 提高组] 愤怒的小N

有序列01序列\(s\)初始为\(0\),每次\(s\)变成\(s\overline s\)\(\overline s\)表示\(s\)取反。进行无数次。

现在求:\(\sum_{i=0}^{n-1} s_if(i)\),其中\(f(x)=\sum_{i=0}^{K-1}a_ix^i\)

\(\lg n\le 5*10^5\)(读入二进制)

\(K\le 500\)


\(g_0(x)=f(x)\)\(g_{k+1}(x)=g_k(x)-g_k(x+2^k)\)

暴力求出\(g_k(x)\),最后再组合一下就能得到答案。这是\(O(K^2\lg n)\)的做法。

如果在比赛的时候打表,可以发现当\(k\ge K\)\(g_k(x)=0\)。于是就可以得到\(O(K^3)\)的做法。

推导一下性质:

\[g_k(x)=\sum_{i=0}^{2^k-1}g_0(x+i)(-1)^{pc(i)}\\ =\sum_{t=0}^{K-1}x^t\sum_{j=0}^{K-t-1}a_{j+t}\binom{j+t}{t}\sum_{i=0}^{2^k-1}(-1)^{pc(i)}i^j \]

探究\(\sum_{i=0}^{2^k-1}(-1)^{pc(i)}i^j\)的性质。设\(P_k(x)=\sum p_ix^i,p_i=(-1)^{pc(i)}[0\le i\le 2^k-1]\)。代入\(P_k(e^x)\)有:

\[P_k(e^x)=\sum p_ie^{ix}\\ =\sum p_i\sum_{j\ge 0} \frac{(ix)^j}{j!}\\ \]

提取\([x^j]\)得到:\([x^j]j!P_k(e^x)=\sum p_i i^j\)。恰好就是我们要求的东西。又有:

\[P_k(x)=\prod_{i=0}^{k-1}(1-x^{2^i})\\ P_k(e^x)=x^k\prod_{i=0}^{k-i}(-\sum_{j\ge 1}\frac{2^{ij}x^{j-1}}{j!}) \]

于是得到:如果\(j<k\),则\([x^j]j!P_k(e^x)=0\)。然后就可以解释前面的结论。


using namespace std;
#include <bits/stdc++.h>
#define L 500005
#define N 1005
#define ll long long
#define mo 1000000007
ll qpow(ll x,ll y=mo-2){
	ll r=1;
	for (;y;y>>=1,x=x*x%mo)
		if (y&1)
			r=r*x%mo;
	return r;	
}
int n,K;
ll nn;
int b[L];
ll pw[N*N],C[N][N],inv[N];
ll g[N][N],f[N];
void init(int n){
	pw[0]=1;
	for (int i=1;i<=n*n;++i)
		pw[i]=pw[i-1]*2%mo;
	for (int i=0;i<=n;++i){
		C[i][0]=1;
		for (int j=1;j<=i;++j)
			C[i][j]=(C[i-1][j-1]+C[i-1][j])%mo;
	}
	inv[1]=1;
	for (int i=2;i<=n;++i)
		inv[i]=(mo-mo/i)*inv[mo%i]%mo;
}
ll getsum0(){
	static ll S[N][N];
	S[0][0]=1;
	for (int i=1;i<=K;++i)
		for (int j=1;j<=i;++j)
			S[i][j]=(S[i-1][j-1]+j*S[i-1][j])%mo;
	ll sum=0;
	for (int k=0;k<K;++k){
		ll tmp=0;
		for (int i=0;i<=k;++i){
			ll pro=1;
			for (int j=nn-i;j<=nn;++j)
				pro=pro*j%mo;
			(tmp+=S[k][i]*inv[i+1]%mo*pro)%=mo;
		}
		(sum+=tmp*g[0][k])%=mo;
	}
	return sum;
}
int main(){
	static char str[L];
	scanf("%s",str);
	n=strlen(str);
	for (int i=0;i<n;++i)
		b[i]=str[n-1-i]-'0';
	for (int i=0,w=1;i<n;++i,w=w*2%mo)
		(nn+=b[i]*w)%=mo;
	scanf("%d",&K);
	for (int i=0;i<K;++i)
		scanf("%d",&g[0][i]);
	init(K);
	ll sum0=getsum0(),sum1=0;
	for (int k=0;k<K;++k)
		for (int j=0;j<K;++j){
			ll sum=0;
			for (int i=j;i<K;++i)
				(sum+=g[k][i]*pw[k*(i-j)]%mo*C[i][j])%=mo;
			g[k+1][j]=(g[k][j]-sum+mo)%mo;
		}
	int c=1;
	for (int k=n-1,of=0;k>=0;--k)
		if (b[k]){
			if (k<K){
				ll sum=0;
				for (int i=0;i<K;++i)
					(sum+=g[k][i]*qpow(of,i))%=mo;
				sum1+=sum*c;
			}
			c*=-1;
			(of+=qpow(2,k))%=mo;
		}
	ll ans=(sum0-sum1)*qpow(2)%mo;
	ans=(ans+mo)%mo;
	printf("%lld\n",ans);
	return 0;
}
posted @ 2021-03-27 19:13  jz_597  阅读(51)  评论(0编辑  收藏  举报