愤怒的小 N
愤怒的小 N
前置知识
-
拉格朗日差值
-
二项式定理
题目描述
极度愤怒的小 N 通关了一款游戏来泄愤。
这款游戏共有 \(n\) 关,分别为第 \(0\) 关、第 \(1\) 关、第 \(2\) 关、\(\cdots\)、第 \(n-1\) 关。这些关卡中有一些是普通关卡,另一些则是奖励关卡。
这款游戏中普通关卡与奖励关卡的分布比较特殊。如果用字符 \(\texttt{a}\) 表示普通关卡,用字符 \(\texttt{b}\) 表示奖励关卡,那么第 \(0\) 关、第 \(1\) 关、第 \(2\) 关、\(\cdots\)、第 \(n-1\) 关依次排列形成的字符串是一个无穷字符串 \(s\) 的前缀,且 \(s\) 可以按照如下方式构造:
-
初始时 \(s\) 为包含单个字符 \(\texttt{a}\) 的字符串。
-
将 \(s\) 的每个字符 \(\texttt{a}\) 替换成字符 \(\texttt{b}\),每个字符 \(\texttt{b}\) 替换成字符 \(\texttt{a}\) 得到字符串 \(t\),然后将 \(t\) 拼接到 \(s\) 后。
-
不断执行2. 得到的字符串就是最终的 \(s\)。
可以发现 \(s=\texttt{abbabaabbaababba}\cdots\),所以这款游戏的第 \(0\) 关是普通关卡,第 \(1\) 关
是奖励关卡,第 \(2\) 关是奖励关卡,第 \(3\) 关是普通关卡,以此类推。
通过游戏的第 \(i\) 关可以得到 \(f(i)\) 分,其中 \(f(x)=a_0+a_1x+a_2x^2+\cdots+a_{k-1}x^{k-1}\)
是一个固定的 \(k-1\) 次多项式。
小 N 通关时一气之下通过了所有奖励关卡而忽略了所有普通关卡,然后就把游戏卸载了。现在回想起来,他想要知道他在卸载游戏前的总得分对 \(10^9+7\) 取模后的结果。
题意转化
首先观察 \(s\) 字符串的性质。\(s\) 中的每一位 \(\text{b}\) 都由上次对应状态的 \(a\) 转来,因为只有 \(\text{b}\) 会产生贡献,不妨设 \(\text{b}\) 为 \(1\),设 \(\text{a}\) 为 \(\text{0}\)。
暴力找 \(s_i\) 一定会超时,那么我们可以观察它的性质。
每次 \(s_i\) 相当于在原来的 \(s_{i-2^m}\) 的基础上在前面加了一个 \(1\),所以这个数的奇偶性会改变。
再根据 \(s_0=0\) 和打表,我们到:
这里有个 \(\bmod 2\),看的好丑,化简一下:
记 \(f(x)=a_0+a_1x+a_2x^2+\cdots+a_{k-1}x^{k-1}=\sum_{i=0}^{k-1}a_i\times x^i\)
所以最终答案为:
我们把这个式子展开得到:
因此我们将式子化为了两部分。先看第一部分。
其中 \(f(i)\) 为 \(k-1\) 次多项式,那么显然 \(\sum_{i=0}^{n-1}f(i)\) 为 \(k\) 次多项式。
- 证明:我们对 \(\sum_{i=0}^{n-1}f(i)\) 作差分,得到每一项为 \(f(i)\),而 \(f(i)\) 为 \(k-1\) 次多项式,根据差分后的多项式的次数为原多项式的次数减一可得 \(\sum_{i=0}^{n-1}f(i)\) 为 \(k\) 次多项式。
记 \(F(x)=\sum_{i=0}^{x-1}f(i)\)
那么我们可以预处理横坐标为 \(0\sim k+1\) 的前缀和,根据拉格朗日插值得出 \(F(x)\)。
- 拉格朗日插值定理
- 拉格朗日插值定理在横坐标连续的时候的公式
然后再看第二部分的贡献。
好像没法化简了?
那么只能把 \(f(i)\) 展开。
交换一下求和的顺序,可得:
现在前面的系数 \(\sum_{r=0}^{k-1}a_r\) 很好求,考虑后面一项要怎么求。
我们考虑把 \(n\) 拆成二进制,对于每个 \(1\) 考虑。
假设有二进制数 \(10001010\),那么它的答案就是 \(10000000+1000+10\) 三部分的贡献。
现在,考虑一段区间 \([ x,x+2^t)\) 我们要求它的贡献。
可得:
我们用 \(\operatorname{sum}(x,t,k)\) 来表示它。
把他化简成从 \(0\) 开始的形式:
根据
可得
设
嗯?后面怎么有 \((i+x)^{k}\),二项式定理展开一下得到:\(\sum_{j=0}^{k}C_{k}^{j}x^{k-j}i^{j}\),即:
继续交换求和顺序,得到:
化简后半部分 \(\sum_{i=0}^{2^t-1}(-1)^{\operatorname{popcount}(i)}\cdot i^{j}\) ,发现这一项是 \(\operatorname{sum}(0,t,j)\)
即:
怎么计算 \(\operatorname{sum}(0,t,j)\) 呢?
这时候我们发现 \(\operatorname{sum}\) 可以递推实现,但是让我们换一种推式子方法。
让我们回顾一下!
代码实现:
#include<bits/stdc++.h>
typedef int Int;
#define int long long
using namespace std;
namespace Testify{
int read(){
int f(1),x(0);
char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-1;
for(;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+(ch^48);
return f*x;
}
void WritE(int x){
if(x<0) putchar('-'),x=-x;
if(x>9) WritE(x/10);
putchar(x%10+48);
}
void write(int x){
WritE(x);
puts("");
}
void Write(int x){
WritE(x);
putchar(' ');
}
}
using namespace Testify;
const int K=505;
const int N=1e6+5;
const int mod=1e9+7;
int iv2=((mod+1)/2);
char ch[N];
inline int qpow(int a,int b){
int res=1;
while(b){
if(b&1){
res=res*a%mod;
}
a=a*a%mod;
b>>=1;
}
return res;
}
int C[505][505],n,m,a[N],ans1,ans2,p2[N];
inline void pre(){
for(register int i=0;i<=m;i++){
C[i][0]=C[i][i]=1;
for(register int j=1;j<i;j++){
C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
}
}
p2[0]=1;
for(register int i=1;i<=n;i++){
p2[i]=2*p2[i-1]%mod;
}
}
inline int niyuan(int x){
return qpow(x,mod-2)%mod;
}
struct lar{
int f(int x){
int res=0;
for(register int i=m-1;i>=0;i--){
res=(res*x+a[i])%mod;
}
return res;
}
int fac[K],facinv[K],xi[K],yi[K],syi[K],nn;
int pv[K],sf[K];
inline void prepr(){
nn=m+1;
fac[0]=facinv[0]=1;
for(register int i=1;i<=nn;i++){
fac[i]=fac[i-1]*i%mod;
facinv[i]=facinv[i-1]*qpow(i,mod-2)%mod;
}
yi[0]=f(0);
for(register int i=1;i<=nn;i++){
xi[i]=i;
yi[i]=(yi[i-1]+f(i))%mod;
int pree=facinv[i-1],suf=facinv[nn-i];
if((nn-i)&1){
suf=(mod-suf);
}
syi[i]=yi[i]*pree%mod*suf%mod;
}
}
inline int F(int x){
x%=mod;
if(x<=nn){
return yi[x];
}
pv[0]=sf[nn+1]=1;
for(register int i=1;i<=nn;i++){
pv[i]=pv[i-1]*(x-xi[i])%mod;
}
for(register int i=nn;i;i--){
sf[i]=sf[i+1]*(x-xi[i])%mod;
}
int res=0;
for(register int i=1;i<=nn;i++){
res=(res+(syi[i]*pv[i-1]%mod*sf[i+1]%mod))%mod;
}
return res%mod;
}
}La;
int memory[505][505];
inline int sum(int t,int k){//计算sum(0,t,k)
if(t>k) return 0;
if(!t) return k==0;
if(memory[t][k]!=-1){
return memory[t][k];
}
int res=sum(t-1,k);
int X=p2[t-1];
int px=1;
for(register int j=k;j>=0;j--,px=px*X%mod){
res=(res+mod-C[k][j]*px%mod*sum(t-1,j)%mod)%mod;
}
memory[t][k]=res;
return res;
}
inline void solve(){
int tnm=0,tt=0;
for(register int i=0;i<n;i++){
int op=(2*tnm+ch[i]-'0')%mod;
int cc=(tt^(ch[i]-'0'));
if(n-i-1<=m){
if(ch[i]=='1'){//划分区间
int X=tnm*p2[n-i]%mod;
for(register int k=0;k<m;k++){
int sm=0;
int px=1;
for(register int j=k;j>=0;j--,px=px*X%mod){
sm=(sm+C[k][j]*px%mod*sum(n-i-1,j)%mod)%mod;
}
if(tt) sm=(mod-sm);
ans2=(ans2+a[k]*sm%mod)%mod;
}
}
}
tnm=op,tt=cc;
}
}
signed main(void){
// freopen("angry.in","r",stdin);
// freopen("angry.out","w",stdout);
memset(memory,-1,sizeof(memory));
scanf("%s",(ch));
n=strlen(ch);
m=read();
for(register int i=0;i<m;i++){
a[i]=read();
}
La.prepr();
pre();
int nm=0;
for(register int i=0;i<n;i++){
nm=(2*nm+ch[i]-'0')%mod;
}
nm=(nm+mod-1)%mod;
ans1=La.F(nm);
solve();
int ans=(ans1+mod-ans2)%mod;
ans=ans*iv2%mod;
write(ans);
return 0;
}
/*
1000
3
3 2 1
*/

浙公网安备 33010602011771号