二次元音游人

这是一股照亮混沌的令和时代互联网的一道光,给在电子的海洋里冲浪的阿宅们带来笑容

愤怒的小 N

愤怒的小 N

前置知识

  • 拉格朗日差值

  • 二项式定理

题目描述

极度愤怒的小 N 通关了一款游戏来泄愤。

这款游戏共有 \(n\) 关,分别为第 \(0\) 关、第 \(1\) 关、第 \(2\) 关、\(\cdots\)、第 \(n-1\) 关。这些关卡中有一些是普通关卡,另一些则是奖励关卡。

这款游戏中普通关卡与奖励关卡的分布比较特殊。如果用字符 \(\texttt{a}\) 表示普通关卡,用字符 \(\texttt{b}\) 表示奖励关卡,那么第 \(0\) 关、第 \(1\) 关、第 \(2\) 关、\(\cdots\)、第 \(n-1\) 关依次排列形成的字符串是一个无穷字符串 \(s\) 的前缀,且 \(s\) 可以按照如下方式构造:

  1. 初始时 \(s\) 为包含单个字符 \(\texttt{a}\) 的字符串。

  2. \(s\) 的每个字符 \(\texttt{a}\) 替换成字符 \(\texttt{b}\),每个字符 \(\texttt{b}\) 替换成字符 \(\texttt{a}\) 得到字符串 \(t\),然后将 \(t\) 拼接到 \(s\) 后。

  3. 不断执行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=\begin{cases} 0 & i=0\\ 1-s_{i-2^m} & i>1 & \text{(其中 } 2^m \text{ 为离 } i \text{ 最近的值,即 highbit。)} \end{cases}\]

暴力找 \(s_i\) 一定会超时,那么我们可以观察它的性质。

每次 \(s_i\) 相当于在原来的 \(s_{i-2^m}\) 的基础上在前面加了一个 \(1\),所以这个数的奇偶性会改变。

再根据 \(s_0=0\) 和打表,我们到:

\[s_i=\operatorname{popcount}(i)\bmod 2 \]

这里有个 \(\bmod 2\),看的好丑,化简一下:

\[\begin{aligned} s_i &=\operatorname{popcount}(i)\bmod 2\\ &= \frac{1}{2}\times(1-(-1)^{\operatorname{popcount}(i)}) \end{aligned}\]

\(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\)

所以最终答案为:

\[\begin{aligned} ans &=\sum_{i=0}^{n-1}f(i)\times s_i\\ &=\sum_{i=0}^{n-1}f(i)\times(\operatorname{popcount}(i)\bmod2)\\ &=\frac{1}{2}\sum_{i=0}^{n-1}f(i)\times (1-(-1)^{\operatorname{popcount}(i)}) \end{aligned}\]

我们把这个式子展开得到:

\[\begin{aligned} ans &=\frac{1}{2}\sum_{i=0}^{n-1}f(i)\times (1-(-1)^{\operatorname{popcount}(i)})\\ &=\frac{1}{2}\sum_{i=0}^{n-1}f(i) - \frac{1}{2}\sum_{i=0}^{n-1}f(i)\times (-1)^{\operatorname{popcount}(i)}\\ &=\frac{1}{2}(\sum_{i=0}^{n-1}f(i) - \sum_{i=0}^{n-1}f(i)\times (-1)^{\operatorname{popcount}(i)})\\ \end{aligned}\]

因此我们将式子化为了两部分。先看第一部分。

\[\sum_{i=0}^{n-1}f(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)\)

  • 拉格朗日插值定理

\[\operatorname{f}(x)=\sum_{i=1}^{n}y_i\prod_{j\ne i}\frac{x-x_i}{x_i-x_j} \]

  • 拉格朗日插值定理在横坐标连续的时候的公式

\[f(x)=\sum\limits_{i=1}^{n+1}y_i\cdot\frac{\prod\limits_{j=1}^{n+1}(x-j)}{(x-i)\cdot(-1)^{n+1-i}\cdot(i-1)!\cdot(n+1-i)!} \]

然后再看第二部分的贡献。

\[\sum_{i=0}^{n-1}f(i)\times (-1)^{\operatorname{popcount}(i)} \]

好像没法化简了?

那么只能把 \(f(i)\) 展开。

\[\sum_{i=0}^{n-1}(-1)^{\operatorname{popcount}(i)}\times \sum_{r=0}^{k-1}a_i\times i^r \]

交换一下求和的顺序,可得:

\[\sum_{r=0}^{k-1}a_r\sum_{i=0}^{n-1}(-1)^{\operatorname{popcount}(i)} i^r \]

现在前面的系数 \(\sum_{r=0}^{k-1}a_r\) 很好求,考虑后面一项要怎么求。

\[\sum_{i=0}^{n-1}(-1)^{\operatorname{popcount}(i)} i^r \]

我们考虑把 \(n\) 拆成二进制,对于每个 \(1\) 考虑。

假设有二进制数 \(10001010\),那么它的答案就是 \(10000000+1000+10\) 三部分的贡献。

现在,考虑一段区间 \([ x,x+2^t)\) 我们要求它的贡献。

可得:

\[\sum_{i=x}^{x+2^t-1}(-1)^{\operatorname{popcount}(i)} i^r \]

我们用 \(\operatorname{sum}(x,t,k)\) 来表示它。

把他化简成从 \(0\) 开始的形式:

\[\sum_{i=0}^{2^t-1}(-1)^{\operatorname{popcount}(i+x)} (i+x)^{r} \]

根据

\[(-1)^{a+b}=(-1)^a\times(-1)^b \]

\[(-1)^{\operatorname{popcount}(a+b)}=(-1)^{\operatorname{popcount}(a)}\times(-1)^{\operatorname{popcount}(b)} \]

可得

\[(-1)^{\operatorname{popcount}(x)} \cdot \sum_{i=0}^{2^t-1}(-1)^{\operatorname{popcount}(i)} (i+u)^{r} \]

\[\operatorname{sum}(x,t,k)=(-1)^{\operatorname{popcount}(x)} \cdot \sum_{i=0}^{2^t-1}(-1)^{\operatorname{popcount}(i)} (i+x)^{k} \]

嗯?后面怎么有 \((i+x)^{k}\),二项式定理展开一下得到:\(\sum_{j=0}^{k}C_{k}^{j}x^{k-j}i^{j}\),即:

\[\operatorname{sum}(x,t,k)=(-1)^{\operatorname{popcount}(x)} \cdot \sum_{i=0}^{2^t-1}(-1)^{\operatorname{popcount}(i)} \sum_{j=0}^{k}C_{k}^{j}x^{k-j}i^{j} \]

继续交换求和顺序,得到:

\[\operatorname{sum}(x,t,k)=(-1)^{\operatorname{popcount}(x)}\cdot \sum_{j=0}^{k}C_{k}^{j}x^{k-j} \sum_{i=0}^{2^t-1}(-1)^{\operatorname{popcount}(i)}\cdot i^{j} \]

化简后半部分 \(\sum_{i=0}^{2^t-1}(-1)^{\operatorname{popcount}(i)}\cdot i^{j}\) ,发现这一项是 \(\operatorname{sum}(0,t,j)\)

即:

\[\operatorname{sum}(x,t,k)=(-1)^{\operatorname{popcount}(x)} \cdot \sum_{j=0}^{k}C_{k}^{j}x^{k-j} \times \operatorname{sum}(0,t,j) \]

怎么计算 \(\operatorname{sum}(0,t,j)\) 呢?

\[\begin{aligned} \operatorname{sum}(0,t,j)&=(-1)^{\operatorname{popcount}(0)} \cdot \sum_{i=0}^{2^t-1}(-1)^{\operatorname{popcount}(i)} i^{j}\\ &=\sum_{i=0}^{2^t-1}(-1)^{\operatorname{popcount}(i)} i^{j}\\ &=\sum_{i=0}^{2^{t-1}-1}(-1)^{\operatorname{popcount}(i)} i^{j} + \sum_{i=2^{t-1}}^{2^{t}-1}(-1)^{\operatorname{popcount}(i)} i^{j} \\ &=\sum_{i=0}^{2^{t-1}-1}(-1)^{\operatorname{popcount}(i)} i^{j} + \sum_{i=0}^{2^{t-1}-1}(-1)^{\operatorname{popcount}(i+2^t-1)} (i+2^{t-1})^{j} \\ &=\operatorname{sum}(0,t-1,j)+\operatorname{sum}(2^t-1,t-1,j) \end{aligned}\]

这时候我们发现 \(\operatorname{sum}\) 可以递推实现,但是让我们换一种推式子方法。

\[\begin{aligned} \operatorname{sum}(0,t,j)&=(-1)^{\operatorname{popcount}(0)} \cdot \sum_{i=0}^{2^t-1}(-1)^{\operatorname{popcount}(i)} i^{j}\\ &=\sum_{i=0}^{2^t-1}(-1)^{\operatorname{popcount}(i)} i^{j}\\ &=\sum_{i=0}^{2^{t-1}-1}(-1)^{\operatorname{popcount}(i)} i^{j} + \sum_{i=2^{t-1}}^{2^{t}-1}(-1)^{\operatorname{popcount}(i)} i^{j} \\ &=\sum_{i=0}^{2^{t-1}-1}(-1)^{\operatorname{popcount}(i)} i^{j} + \sum_{i=0}^{2^{t-1}-1}(-1)^{\operatorname{popcount}(i+2^t-1)} (i+2^{t-1})^{j} \\ &=\operatorname{sum}(0,t-1,j)+\sum_{i=0}^{2^{t-1}-1}(-1)^{\operatorname{popcount}(i+2^t-1)} (i+2^{t-1})^{j} \\ &=\operatorname{sum}(0,t-1,j)+(-1)^{\operatorname{popcount}(2^t-1)}\cdot \sum_{i=0}^{2^{t-1}-1}(-1)^{\operatorname{popcount}(i)} (i+2^{t-1})^{j} \\ &=\operatorname{sum}(0,t-1,j)+ \sum_{i=0}^{2^{t-1}-1}(-1)^{\operatorname{popcount}(i)} (i+2^{t-1})^{j} \\ &=\operatorname{sum}(0,t-1,j)+ \sum_{i=0}^{2^{t-1}-1}(-1)^{\operatorname{popcount}(i)} (i+2^{t-1})^{j} \\ &\text{类似上面,二项式定理展开再交换顺序}\\ &=\operatorname{sum}(0,t-1,j)+ \sum_{i=0}^{j} C_j^s(2^{t-1})^{j-s}\operatorname{sum}(0,t-1,s) \\ \end{aligned}\]


让我们回顾一下!

\[\begin{aligned} ans &=\frac{1}{2}\sum_{i=0}^{n-1}f(i)\times (1-(-1)^{\operatorname{popcount}(i)})\\ &=\frac{1}{2}\sum_{i=0}^{n-1}f(i) - \frac{1}{2}\sum_{i=0}^{n-1}f(i)\times (-1)^{\operatorname{popcount}(i)}\\ \end{aligned}\]


\[ans1=\sum_{i=0}^{n-1}f(i) \text{(用拉插实现)} \]


\[\begin{aligned} ans2&=\sum_{i=0}^{n-1}f(i)\times (-1)^{\operatorname{popcount}(i)}\\ ans2&=\sum \operatorname{sum}(x,t,k) \end{aligned}\]


\[ans=\frac{1}{2}(ans1-ans2) \]

代码实现:

#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

*/
posted @ 2023-10-20 07:24  超绝最可爱天使酱  阅读(37)  评论(0)    收藏  举报