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;
}