[NOI Online 2021 提高组] 愤怒的小N

暴力

倍增维护[x,x+2d)内部所有b的权值和 以及a的,用多项式表示

具体的,维护两个多项式F0(x),F1(x),每次倍增的转移如下

F0(x)F0(x)+F1(x+d)

F1(x)F1(x)+F0(x+d)

因此暴力倍增复杂度为O(nk2),实现上需要记录每次倍增之后多项式与答案的前面部分相拼接需要额外的偏移

 

优化

如果你输出多项式,就会发现,倍增k次之后,所有a,b位置对应的多项式就完全相同了

形式化地理解这个过程

一开始,F0(x)=A(x),F1(x)=0,其中A(x)为读入的多项式

进行一次转移后,F0(x),F1(x)的第k1项只受到对方的k1项和自己的k1项影响

因此一次转移后[xk1]F0(x)=[xk1]F1(x)

下一次转移,第k2项值只受到对方的k2项,已经已经确定相同的k1项影响

这个过程不断进行,第i次倍增会使得[ki,k1]项相同

 

对于k次倍增之后,后面多出来的部分,可以直接求一个多项式前缀和,然后除2得到答案

多项式前缀和容易通过拉格朗日插值解决,复杂度为O(k2)

预处理前面的多项式复杂度为O(k3),求后面的式子为O(k2),预处理n的值复杂度为O(logn)

因此复杂度为O(logn+k3)

 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef pair <int,int> Pii;
#define reg register
#define mp make_pair
#define pb push_back
#define Mod1(x) ((x>=P)&&(x-=P))
#define Mod2(x) ((x<0)&&(x+=P))
#define rep(i,a,b) for(int i=a,i##end=b;i<=i##end;++i)
#define drep(i,a,b) for(int i=a,i##end=b;i>=i##end;--i)
template <class T> inline void cmin(T &a,T b){ ((a>b)&&(a=b)); }
template <class T> inline void cmax(T &a,T b){ ((a<b)&&(a=b)); }

char IO;
template <class T=int> T rd(){
T s=0; int f=0;
while(!isdigit(IO=getchar())) f|=IO=='-';
do s=(s<<1)+(s<<3)+(IO^'0');
while(isdigit(IO=getchar()));
return f?-s:s;
}

const int N=5e5+10,M=510,INF=1e9+10,P=1e9+7;

int n,m;
char s[N];
int D[N],T[N];
// D[i]预处理倍增求出的每项对于答案贡献时存在的偏移
// T[i]预处理每个位后面1的个数
int A[N],F[2][M],G[2][M],C[M][M];
int Pow[N];
ll qpow(ll x,ll k=P-2){
ll res=1;
for(;k;k>>=1,x=x*x%P) if(k&1) res=res*x%P;
return res;
}
int X[M],Y[M];
int Lagrange(int x,int n,int *X,int *Y){
int ans=0;
rep(i,0,n) {
ll s=1;
rep(j,0,n) if(i!=j) s=s*(X[i]-X[j])%P;
s=qpow(s);
rep(j,0,n) if(i!=j) s=s*(x-X[j])%P;
ans=(ans+s*Y[i])%P;
}
return ans;
}

int main(){
freopen("angry.in","r",stdin),freopen("angry.out","w",stdout);
scanf("%s",s),n=strlen(s),reverse(s,s+n);
m=rd();
rep(i,0,m-1) A[i]=F[0][i]=rd();
rep(i,0,m) rep(j,*C[i]=1,i) C[i][j]=(C[i-1][j]+C[i-1][j-1])%P;
int ans=0,x=2;
rep(i,1,n-1) D[i-1]=x*(s[i]=='1'),x=x*2%P,T[i-1]=(s[i]=='1');
drep(i,n-1,0) D[i]+=D[i+1],Mod1(D[i]),T[i]^=T[i+1];
x=1;
rep(i,0,min(m-1,n-1)) {
if(s[i]=='1') {
int t=1;
rep(j,0,m-1) {
ans=(ans+1ll*t*F[!T[i]][j])%P;
t=1ll*t*D[i]%P;
}
}
rep(d,0,1) rep(j,0,m-1) G[d][j]=F[d][j];
rep(j,*Pow=1,m-1) Pow[j]=1ll*Pow[j-1]*x%P;
rep(j,0,m-1) rep(k,0,j) {
F[0][k]=(F[0][k]+1ll*C[j][k]*Pow[j-k]%P*G[1][j])%P;
F[1][k]=(F[1][k]+1ll*C[j][k]*Pow[j-k]%P*G[0][j])%P;
}
x=x*2%P;
}
// 倍增到前k-1项
if(m>=n) return printf("%d\n",ans),0;
// 预处理拉格朗日插值
rep(i,0,m) {
X[i]=i,x=1;
rep(j,0,m-1) {
Y[i]=(Y[i]+1ll*A[j]*x)%P;
x=1ll*x*i%P;
}
if(i) Y[i]+=Y[i-1],Mod1(Y[i]);
}
ans=(ans+1ll*Lagrange(D[m-1]-1,m,X,Y)*(P+1)/2)%P;
printf("%d\n",ans);
}