P7468 [NOI Online 2021 提高组] 愤怒的小N
小 N N N在玩一款游戏。这款游戏中有 n n n个关卡,分别为第 0 0 0关,第 1 1 1关, … \dots …,第 n − 1 n-1 n−1关。这些关卡中有一部分是普通关卡,另一部分则是奖励关卡。
这款游戏中普通关卡与奖励关卡的分布比较特殊。如果用字符 a a a代表普通关卡,用字符 b b b代表奖励关卡,那么第 0 0 0关,第 1 1 1关, … \dots …,第 n − 1 n-1 n−1关依次排列形成的字符串是一个无穷字符串 s s s的的前缀,且 s s s可以按照以下方式构造:
可以发现 s = a b b a b a a b b a a b a b b a ⋯ s=abbabaabbaababba\cdots s=abbabaabbaababba⋯。
通过第 i i i关可以得到 f ( i ) f(i) f(i)分,其中 f ( i ) = a 0 + a 1 x + a 2 x 2 + ⋯ + a k − 1 x k − 1 f(i)=a_0+a_1x+a_2x^2+\cdots+a_{k-1}x^{k-1} f(i)=a0+a1x+a2x2+⋯+ak−1xk−1。
小 N N N通过了所有奖励关卡,求他得到的分数模 1 0 9 + 7 10^9+7 109+7后的值。
为了方便, n n n以二进制的形式给出。
0 ≤ log 2 n < 5 × 1 0 5 , 1 ≤ k ≤ 500 , 0 ≤ a i < 1 0 9 + 7 , a k − 1 ≠ 0 0\leq \log_2n<5\times 10^5,1\leq k\leq 500,0\leq a_i<10^9+7,a_{k-1}\neq 0 0≤log2n<5×105,1≤k≤500,0≤ai<109+7,ak−1=0
首先,我们发现,假设一个关卡的编号为 x x x,则如果去掉 x x x的最高位,则 x x x会从奖励关卡变为普通关卡或从普通关卡变为奖励关卡,所以当 x x x的二进制的各位之和为奇数时第 x x x关为奖励关卡,否则 x x x为普通关卡。
考虑 D P DP DP,设 f i , j , 0 / 1 f_{i,j,0/1} fi,j,0/1表示在编号为 0 ∼ 2 i − 1 0\sim 2^i-1 0∼2i−1的位置中,所有普通关卡/奖励关卡的编号的 j j j次方和。根据二项式展开可得转移式为
f i , j , v = f i − 1 , j , v + ∑ l = 0 j ( j l ) × f i − 1 , l , 1 − v × 2 ( i − 1 ) ( j − l ) f_{i,j,v}=f_{i-1,j,v}+\sum\limits_{l=0}^j\binom jl\times f_{i-1,l,1-v}\times 2^{(i-1)(j-l)} fi,j,v=fi−1,j,v+l=0∑j(lj)×fi−1,l,1−v×2(i−1)(j−l)
在计算答案的时候,我们用类似树状数组的思想,按次数从小到大计算 n n n的二进制位中每个 1 1 1的贡献。设这一位为 i i i,枚举 f ( x ) f(x) f(x)中每一项,根据二项式展开用 f i , j , k f_{i,j,k} fi,j,k来得到贡献,那么答案为
a n s + = ∑ j = 0 k − 1 a j ∑ l = 0 j ( j l ) × f i , l , 1 − v × t m p j − l ans+=\sum\limits_{j=0}^{k-1}a_j\sum\limits_{l=0}^j\binom jl\times f_{i,l,1-v}\times tmp^{j-l} ans+=j=0∑k−1ajl=0∑j(lj)×fi,l,1−v×tmpj−l
其中 v v v表示高于第 i i i位的 1 1 1的数量,这样能保证当前求的是奖励关卡。 t m p tmp tmp表示高于第 i i i位的部分转化为十进制后的值。
这样做是 O ( log n k 2 ) O(\log nk^2) O(lognk2)的,我们考虑优化。
通过数学归纳法或者打表找规律,我们可以得到:
设 t m p tmp tmp表示高于第 k k k位的部分构成的数的值,则对于编号小于等于 t m p tmp tmp的奖励关卡,我们可以直接用 ∑ i = 0 t m p f ( i ) 2 \dfrac{\sum\limits_{i=0}^{tmp}f(i)}{2} 2i=0∑tmpf(i)来求。因为 f ( i ) f(i) f(i)是一个 k − 1 k-1 k−1次的多项式,所以其前缀和也是一个 k − 1 k-1 k−1次的多项式,用拉格朗日差值法即可 O ( k 2 ) O(k^2) O(k2)得到 ∑ i = 0 t m p f ( i ) \sum\limits_{i=0}^{tmp}f(i) i=0∑tmpf(i),再除以 2 2 2即可得到这部分的贡献。
对于剩下的部分,按前面所说的方法计算前 k − 1 k-1 k−1位对答案的贡献,时间复杂度为 O ( log n + k 3 ) O(\log n+k^3) O(logn+k3)。
总时间复杂度为 O ( log n + k 3 ) O(\log n+k^3) O(logn+k3)。
#include
using namespace std;
const int N=500000,K=500;
const long long mod=1000000007,ny=500000004;
int n,k,c1;
long long ans=0,a[K+5],pw[K+5],x[K+5],y[K+5],C[K+5][K+5],f[K+5][K+5][2];
char c[N+5];
long long mi(long long t,long long v){
if(!v) return 1;
long long re=mi(t,v/2);
re=re*re%mod;
if(v&1) re=re*t%mod;
return re;
}
void init(){
C[0][0]=1;
for(int i=1;i<=K;i++){
C[i][0]=C[i][i]=1;
for(int j=1;j<i;j++) C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
}
long long now=0;
for(int i=0;i<=K;i++){
x[i]=i;y[i]=now;
long long sum=0;
for(int j=K-1;j>=0;j--) sum=(sum*i+a[j])%mod;
now=(now+sum)%mod;
}
}
long long gt(long long vx){
long long re=0;
for(int i=0;i<=K;i++){
long long p=y[i],q=1;
for(int j=0;j<=K;j++){
if(i==j) continue;
p=p*((vx-x[j]+mod)%mod)%mod;
q=q*((x[i]-x[j]+mod)%mod)%mod;
}
re=(re+p*mi(q,mod-2)%mod)%mod;
}
return re;
}
int main()
{
// freopen("angry.in","r",stdin);
// freopen("angry.out","w",stdout);
scanf("%s",c);
c1=strlen(c);
reverse(c,c+c1);
scanf("%d",&k);
for(int i=0;i<k;i++){
scanf("%lld",&a[i]);
}
init();
f[0][0][0]=1;pw[0]=1;
for(int i=1,tmp=1;i<k;i++,tmp=tmp*2%mod){
for(int j=1;j<k;j++) pw[j]=pw[j-1]*tmp%mod;
for(int j=0;j<k;j++){
f[i][j][0]=f[i-1][j][0];
f[i][j][1]=f[i-1][j][1];
for(int l=0;l<=j;l++){
f[i][j][0]=(f[i][j][0]+C[j][l]*f[i-1][l][1]%mod*pw[j-l]%mod)%mod;
f[i][j][1]=(f[i][j][1]+C[j][l]*f[i-1][l][0]%mod*pw[j-l]%mod)%mod;
}
}
}
for(int i=c1-1,tmp=0,w=0;i>=0;i--){
if(c[i]=='0') continue;
if(i<k){
for(int j=1;j<k;j++) pw[j]=pw[j-1]*tmp%mod;
for(int j=0;j<k;j++){
long long sum=0;
for(int l=0;l<=j;l++){
sum=(sum+C[j][l]*f[i][l][w^1]%mod*pw[j-l]%mod)%mod;
}
ans=(ans+sum*a[j])%mod;
}
}
tmp=(tmp+mi(2,i))%mod;w^=1;
}
long long tmp=0;
for(int i=c1-1;i>=k;i--)
if(c[i]=='1') tmp=(tmp+mi(2,i))%mod;
ans=(ans+gt(tmp)*ny%mod)%mod;
printf("%lld",ans);
return 0;
}