【NOI Online 2021 提高组】愤怒的小 N(生成函数,结论)

\(n=\log m\),记 \(m\) 在二进制表示下为 \(\overline{s_{n-1}\cdots s_1s_0}\)

首先可以归纳得到一个位置 \(x\)\(\texttt{b}\) 当且仅当 \(\operatorname{popc}(x)\equiv 1\pmod 2\),那么我们要统计的即为 \(\sum_{x=0}^{m-1}[\operatorname{popc}(x)\& 1]f(x)\)

考虑用类似数位 DP 的方式来解决这个问题。这样我们需要先知道 \(f_{n,k,0/1}\) 表示 \(x\in[0,2^n-1]\)\(\operatorname{popc}(x)\)\(0/1\)\(x\)\(k\) 次方和,然后再利用 \(f\) 推出 \(x<m\) 的所有合法的 \(x\) 的答案。可以做到 \(O(nk^2)\)

统计合法的 \(x\)\(k\) 次方和的一个常见思路是生成函数。 例如,若我们要统计 \(S\) 集合内的数的 \(k\) 次方和,那么其 EGF 为:

\[F_S(z)=\sum_{x\in S}e^{xz} \]

使用 EGF 而不是 OGF 的原因是方便运算和二项式卷积,自然数幂前缀和的生成函数选用 EGF 也是如此。

那么设 \(P_n(z),Q_n(z)\) 分别为 \([0,2^n-1]\)\(\operatorname{popc}(x)\)\(0/1\)\(x\)\(k\) 次方和的 EGF。考虑转移,我们要将所有合法的 \(x\) 加上某个常数 \(b\),这表现在生成函数上:

\[F_{S+b}(z)=\sum_{x\in S}e^{(x+b)z}=F_S(z)e^{bz} \]

那么就容易得到转移:

\[\begin{aligned} P_{n+1}(z)&=P_{n}(z)+Q_{n}(z)e^{2^nz}\\ Q_{n+1}(z)&=Q_{n}(z)+P_{n}(z)e^{2^nz} \end{aligned} \]

一个神奇的结论是,若我们考察 \(P_{n+1}(z)-Q_{n+1}(z)\)

\[\begin{aligned} P_{n+1}(z)-Q_{n+1}(z)&=(P_{n}(z)-Q_{n}(z))(1-e^{2^nz})\\ P_{0}(z)-Q_{0}(z)&=1 \end{aligned} \]

发现 \(P_n(z)-Q_n(z)\) 的次数小于 \(n\) 的项都为 \(0\),那么 \(P_n(z)\equiv Q_n(z)\equiv \frac{1}{2}S_n(z)\pmod {z^{n}}\),其中 \(S_n(z)\)\([0,2^n-1]\) 的自然数幂和的 EGF。

直观上的感受就是,对于任意 \(n> k\),始终满足 \(f_{n,k,0}=f_{n,k,1}=\frac{\sum_{0\leq x\leq 2^n-1}x^k}{2}\) 再观察我们要求的:

\[\begin{aligned} ans_k&=\sum_{x<m}[\operatorname{popc}(x)\& 1]x^k\\ &=[z^k]\sum_{i=0}^{n-1}[s_i=1]e^{\overline{s_{n-1}\cdots s_{i+1}0\cdots 0}\ z} \begin{cases} Q_{i}(z)&\text{if }s_{n-1}+\cdots+s_{i+1}\equiv 0\pmod 2\\ P_{i}(z)&\text{if }s_{n-1}+\cdots+s_{i+1}\equiv 1\pmod 2 \end{cases}\\ &=[z^k]\sum_{i=0}^{k}[s_i=1]e^{\overline{s_{n-1}\cdots s_{i+1}0\cdots 0}\ z} \begin{cases} Q_{i}(z)&\text{if }s_{n-1}+\cdots+s_{i+1}\equiv 0\pmod 2\\ P_{i}(z)&\text{if }s_{n-1}+\cdots+s_{i+1}\equiv 1\pmod 2 \end{cases}\\ &+[z^k]\frac{1}{2}\sum_{i=k+1}^{n-1}[s_i=1]e^{\overline{s_{n-1}\cdots s_{i+1}0\cdots 0}\ z}S_{i}(z)\\ &=[z^k]\sum_{i=0}^{k}[s_i=1]e^{\overline{s_{n-1}\cdots s_{i+1}0\cdots 0}\ z} \begin{cases} Q_{i}(z)&\text{if }s_{n-1}+\cdots+s_{i+1}\equiv 0\pmod 2\\ P_{i}(z)&\text{if }s_{n-1}+\cdots+s_{i+1}\equiv 1\pmod 2 \end{cases}\\ &+\frac{1}{2}\sum_{x<\overline{s_{n-1}\cdots s_{p+1}10\cdots0}}x^k \end{aligned} \]

其中 \(p=\min\{p|p\in[k+1,n-1]\land s_p=1\}\)

这样就好求了,对于前者,我们先根据一开始的 DP 式预处理出 \(P_{i-1}(z),Q_{i-1}(z)\),然后对于每个 \(i\) 再暴力 \(O(k^2)\) 乘上 \(e^{\overline{s_{n-1}\cdots s_{i+1}0\cdots 0}\ z}\);对于后者,是普通的自然数幂求和,对于每个 \(k\) 可以 \(O(k)\) 解决。

瓶颈在前半部分,时间复杂度 \(O(n+k^3)\)(当然可以用卷积优化到 \(O(n+k^2\log k)\))。

#include<bits/stdc++.h>

#define K 510
#define N 500010

using namespace std;

namespace modular
{
	const int mod=1000000007,inv2=(mod+1)>>1;
	inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
	inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
	inline int mul(int x,int y){return 1ll*x*y%mod;}
	inline void Add(int &x,int y){x=x+y>=mod?x+y-mod:x+y;}
	inline void Dec(int &x,int y){x=x-y<0?x-y+mod:x-y;}
	inline void Mul(int &x,int y){x=1ll*x*y%mod;}
	inline int poww(int a,int b){int ans=1;for(;b;Mul(a,a),b>>=1)if(b&1)Mul(ans,a);return ans;}
}using namespace modular;

int n,k,fac[K],ifac[K],ans[K],C[K][K];
int val[N],pw2[N];
char str[N];
bool s[N];

void init()
{
	reverse(str,str+n);
	for(int i=0;i<n;i++) s[i]=str[i]-'0';
	fac[0]=1;
	for(int i=1;i<=k+1;i++) fac[i]=mul(fac[i-1],i);
	ifac[k+1]=poww(fac[k+1],mod-2);
	for(int i=k+1;i>=1;i--) ifac[i-1]=mul(ifac[i],i);
	pw2[0]=1;
	for(int i=1;i<n;i++) pw2[i]=add(pw2[i-1],pw2[i-1]);
	int sum=0;
	for(int i=0;i<n;i++) if(s[i]) Add(sum,pw2[i]);
	val[0]=sum;
	for(int i=1;i<n;i++) val[i]=(s[i-1]?dec(val[i-1],pw2[i-1]):val[i-1]);
	for(int i=0;i<=k;i++)
	{
		C[i][0]=1;
		for(int j=1;j<=i;j++)
			C[i][j]=add(C[i-1][j-1],C[i-1][j]);
	}
}

namespace sub1
{
	using modular::Add;
	int f[K][2][K],tmp[K];
	inline void Add(int *f,int *g)
	{
		for(int i=0;i<=k;i++) Add(f[i],g[i]);
	}
	inline void Trans(int *f,int *g,int b)
	{
		static int pwb[K];
		pwb[0]=1;
		for(int i=1;i<=k;i++) pwb[i]=mul(pwb[i-1],b);
		for(int i=k;i>=0;i--)
		{
			g[i]=f[i];
			for(int j=0;j<i;j++)
				Add(g[i],mul(mul(C[i][j],pwb[i-j]),f[j]));
		}
	}
	void main()
	{
		f[0][0][0]=1;
		for(int i=0;i<min(n-1,k);i++)
			for(int j=0;j<2;j++)
				Trans(f[i][j^1],f[i+1][j],pw2[i]),Add(f[i+1][j],f[i][j]);
		bool popc=0;
		for(int i=0;i<n;i++) if(s[i]) popc^=1;
		for(int i=0;i<=min(n-1,k);i++)
		{
			if(!s[i]) continue; popc^=1;
			Trans(f[i][popc^1],tmp,val[i+1]),Add(ans,tmp);
		}
	}
}

namespace sub2
{
	int y[K][K];
	int lagrange(int *y,int n,int x)
	{
		static int pre[K],suf[K];
		pre[0]=suf[n+2]=1;
		for(int i=1;i<=n+1;i++) pre[i]=mul(pre[i-1],dec(x,i));
		for(int i=n+1;i>=1;i--) suf[i]=mul(suf[i+1],dec(x,i));
		int ans=0;
		for(int i=1;i<=n+1;i++) Add(ans,mul(y[i],mul(mul(pre[i-1],suf[i+1]),mul(ifac[i-1],((n+1-i)&1)?dec(0,ifac[n+1-i]):ifac[n+1-i]))));
		return ans;
	}
	void main()
	{
		if(n-1<k+1) return;
		int p=-1;
		for(int i=k+1;i<n;i++) if(s[i]){p=i;break;}
		for(int i=1;i<=k+2;i++)
			for(int j=0,now=1;j<=k;j++,Mul(now,i-1)) y[j][i]=now;
		for(int i=0;i<=k;i++)
		{
			for(int j=1;j<=k+2;j++) Add(y[i][j],y[i][j-1]);
			Add(ans[i],mul(inv2,lagrange(y[i],i+1,val[p])));
		}
	}
}

int main()
{
	scanf("%s%d",str,&k);n=strlen(str);k--;
	init();
	sub1::main();
	sub2::main();
	int sum=0;
	for(int i=0;i<=k;i++)
	{
		int a;scanf("%d",&a);
		Add(sum,mul(a,ans[i]));
	}
	printf("%d\n",sum);
	return 0;
}
posted @ 2022-10-29 11:05  ez_lcw  阅读(22)  评论(0)    收藏  举报