【学习笔记】[NOI Online 2021 提高组] 愤怒的小 N

看来我离 N O I NOI NOI的赛场还远

考虑多项式平移的复杂度,暴力计算是 O ( n k 2 ) O(nk^2) O(nk2)的,难以优化

还是不知道该从何入手 ,因为提到幂和我只能想到斯特林数

这题告诉我们另一种方法:反复利用 二项式定理

它基于一个非常简单的事实:若 ∑ x i p = ∑ y i p \sum x_i^p=\sum y_i^p xip=yip 对任意 0 ≤ p ≤ k 0\le p\le k 0pk成立,那么 ∑ ( x i + c ) k = ∑ ( y i + c ) k \sum (x_i+c)^k=\sum (y_i+c)^k (xi+c)k=(yi+c)k

那么大致上可以猜到结论了。

观察这样一个式子: ∑ 0 ≤ i < 2 k ( − 1 ) popcount(i) i k − 1 \sum_{0\le i<2^k}(-1)^\text{popcount(i)}i^{k-1} 0i<2k(1)popcount(i)ik1。考虑 i k − 1 i^{k-1} ik1的组合意义:将 k − 1 k-1 k1个不同的球放进 i i i个不同的盒子中。前面那个像极了容斥系数,但是少了一个组合数。这个思路最大的问题在于没有用到 0 ≤ i < 2 k 0\le i<2^k 0i<2k这个限制。

不妨换一种思路。相当于我们有大小为 2 0 , 2 1 , . . . , 2 k − 1 2^0,2^1,...,2^{k-1} 20,21,...,2k1 k k k种盒子,但是一共只有 k − 1 k-1 k1个球,那么必然有盒子会空,那么上式就等价于去枚举占据了哪些盒子并容斥计算,这样方案数算出来恰好为 0 0 0

这样我们得到了一般的结论:对于任意 0 ≤ p < k 0\le p0p<k都有 ∑ 0 ≤ i < 2 k ( − 1 ) popcount(i) i p = 0 \sum_{0\le i<2^k}(-1)^{\text{popcount(i)}}i^p=0 0i<2k(1)popcount(i)ip=0

由此我们可以用拉格朗日插值法计算关于幂和的多项式。

回到统计答案的部分,假设之前高位的和为 X X X,要计算: f ( X + c ) = ∑ 0 ≤ i < k a i ( X + c ) i = ∑ i < k a i ∑ j ≤ i ( i j ) X j c i − j = ∑ 0 ≤ j < k c j ∑ j ≤ i < k a i ( i j ) X i − j f(X+c)=\sum_{0\le if(X+c)=0i<kai(X+c)i=i<kaiji(ji)Xjcij=0j<kcjji<kai(ji)Xij

这样虽然可以处理出 ∑ 0 ≤ c < 2 x , c ∈ C 1 c j \sum_{0\le c<2^x,c\in C_1}c^j 0c<2x,cC1cj的值,但是算答案的复杂度还是 O ( n k 2 ) O(nk^2) O(nk2)

考虑再用一次结论。那么 f ( X + c ) = 1 2 ∑ 0 ≤ c < 2 x f ( X + c ) f(X+c)=\frac{1}{2}\sum_{0\le c<2^x}f(X+c) f(X+c)=210c<2xf(X+c),注意到 求和的下标是连续的 ,所以将 f f f的前缀和对应的多项式 g g g用拉格朗日插值法求出来即可。

直接暴力计算多项式的复杂度好像不优,考虑 多项式求逆 ,这样可以在 O ( k 2 ) O(k^2) O(k2)的时间内处理出 g g g。最后统计答案的复杂度是 O ( n k ) O(nk) O(nk)的。

然后发现其实只用算一次多项式点值就好了。这个不太好说。

复杂度 O ( n + k 3 ) O(n+k^3) O(n+k3)

要加 inline \text{inline} inline卡常。

remark \text{remark} remark 感觉难点还是在于从二项式定理的角度发现结论

#include
#define fi first
#define se second
#define ll long long
#define pb push_back
#define db double
#define inf 0x3f3f3f3f
using namespace std;
const int mod=1e9+7;
const int N=505;
const int M=5e5+5;
string str;
int n,K;
ll a[N],f[N],Y[N],g[N],g2[N],fac[N],inv[N],invnum[N];
ll fpow(ll x,ll y=mod-2){
    ll z(1);
    for(;y;y>>=1){
        if(y&1)z=z*x%mod;
        x=x*x%mod;
    }return z;
}
void add(ll &x,ll y){if((x+=y)>=mod)x-=mod;}
ll calc(ll x){
    x%=mod;ll res(0),y(1);
    for(int i=0;i<K;i++)add(res,a[i]*y%mod),y=y*x%mod;
    return res;
}
ll calc2(ll x){
    ll res(0),y(1);
    for(int i=0;i<=K;i++)add(res,f[i]*y%mod),y=y*x%mod;
    return res;
}
void init(){
    Y[0]=a[0];for(int i=1;i<=K;i++)Y[i]=(Y[i-1]+calc(i))%mod;
    for(int i=0;i<=K+1;i++)g[i]=0;g[0]=1;
    for(int i=0;i<=K;i++){
        for(int j=K+1;j>=0;j--){
            g[j]=mod-g[j]*i%mod;
            if(j>0)add(g[j],g[j-1]);
        }
    }
    for(int i=0;i<=K;i++){
        ll mul=Y[i]*inv[K-i]%mod*inv[i]%mod;
        if(K-i&1)mul*=-1;
        if(i){
            for(int j=0;j<=K;j++){
                g2[j]=mod-g[j];
                if(j>0)add(g2[j],g2[j-1]);
                g2[j]=g2[j]*invnum[i]%mod;
                add(f[j],g2[j]*mul%mod);
            }   
        }
        else{
            for(int j=0;j<=K;j++){
                g2[j]=g[j+1];
                add(f[j],g2[j]*mul%mod);
            }
        }
    }
}
ll fac2[M],res;
ll ps[N][N][2];
ll binom(int x,int y){
    if(x<0||y<0||x<y)return 0;
    return fac[x]*inv[y]%mod*inv[x-y]%mod;
}
void init2(){
    for(int i=1;i<=K*K;i++)fac2[i]=fac2[i-1]*2%mod;
    ps[0][0][0]=1;
    for(int i=1;i<=K;i++){
        for(int k=0;k<=K;k++){
            add(ps[i][k][0],ps[i-1][k][0]);
            add(ps[i][k][1],ps[i-1][k][1]);
            for(int l=0;l<=k;l++){
                add(ps[i][k][0],ps[i-1][l][1]*binom(k,l)%mod*fac2[(i-1)*(k-l)]%mod);
                add(ps[i][k][1],ps[i-1][l][0]*binom(k,l)%mod*fac2[(i-1)*(k-l)]%mod);
            }
        }
    }
}
int main(){
    ios::sync_with_stdio(false);
    cin.tie(0),cout.tie(0);
    cin>>str>>K,n=str.size();for(int i=0;i<K;i++)cin>>a[i];
    fac2[0]=1;for(int i=1;i<=n;i++)fac2[i]=fac2[i-1]*2%mod;
    fac[0]=1;for(int i=1;i<=K;i++)fac[i]=fac[i-1]*i%mod;
    inv[K]=fpow(fac[K]);for(int i=K;i>=1;i--)inv[i-1]=inv[i]*i%mod;
    for(int i=1;i<=K;i++)invnum[i]=inv[i]*fac[i-1]%mod;invnum[2]=(mod+1)/2;
    init();init2();reverse(str.begin(),str.end());
    ll num=0;int f=1;
    for(int x=n-1;x>=0;x--){
        if(str[x]=='1'){
            if(x>=K){
                if(x==n-1)add(res,calc2(num+fac2[x]-1)*invnum[2]%mod);
                else add(res,(calc2(num+fac2[x]-1)-calc2(num-1)+mod)*invnum[2]%mod);
            }
            else{
                for(int j=0;j<K;j++){
                    ll mul=1;
                    for(int i=j;i<K;i++){
                        add(res,ps[x][j][f]*binom(i,j)%mod*a[i]%mod*mul%mod);
                        mul=mul*num%mod;
                    }
                }
            }
            add(num,fac2[x]),f^=1;
        }
    }
    res=(res+mod)%mod;
    cout<<res<<"\n";
}

你可能感兴趣的:(学习,笔记)