【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 为:
使用 EGF 而不是 OGF 的原因是方便运算和二项式卷积,自然数幂前缀和的生成函数选用 EGF 也是如此。
那么设 \(P_n(z),Q_n(z)\) 分别为 \([0,2^n-1]\) 中 \(\operatorname{popc}(x)\) 为 \(0/1\) 的 \(x\) 的 \(k\) 次方和的 EGF。考虑转移,我们要将所有合法的 \(x\) 加上某个常数 \(b\),这表现在生成函数上:
那么就容易得到转移:
一个神奇的结论是,若我们考察 \(P_{n+1}(z)-Q_{n+1}(z)\):
发现 \(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}\)。 再观察我们要求的:
其中 \(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;
}