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

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

暴力

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

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

\(F_0(x)\leftarrow F_0(x)+F_1(x+d)\)

\(F_1(x)\leftarrow F_1(x)+F_0(x+d)\)

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

\[\ \]

优化

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

形式化地理解这个过程

一开始,\(F_0(x)=A(x),F_1(x)=0\),其中\(A(x)\)为读入的多项式

进行一次转移后,\(F_0(x),F_1(x)\)的第\(k-1\)项只受到对方的\(k-1\)项和自己的\(k-1\)项影响

因此一次转移后\([x^{k-1}]F_0(x)=[x^{k-1}]F_1(x)\)

下一次转移,第\(k-2\)项值只受到对方的\(k-2\)项,已经已经确定相同的\(k-1\)项影响

这个过程不断进行,第\(i\)次倍增会使得\([k-i,k-1]\)项相同

\[\ \]

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

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

预处理前面的多项式复杂度为\(O(k^3)\),求后面的式子为\(O(k^2)\),预处理\(n\)的值复杂度为\(O(\log n)\)

因此复杂度为\(O(\log n+k^3)\)

\[\ \]

#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);
}
posted @ 2021-03-27 15:19  chasedeath  阅读(179)  评论(5编辑  收藏  举报