luogu P7468 [NOI Online 2021 提高组] 愤怒的小N(民间数据)
题面传送门
在看这篇题解之前你需要先掌握的知识是:二项式定理,拉格朗日插值,强大的推式子能力
我们定义\(g(n)\)为\(n\)在二进制表达下\(1\)的个数。
我们要求的就是这样一个式子\(\sum\limits_{i=0}^{n-1}{f(i)\times (g(i)\bmod 2)}\)
考虑这个东西其实不是很好搞,对其进行扰动,得到\(\frac{1}{2}\sum\limits_{i=0}^{n-1}{(1-(-1)^{g(n)})}f(i)\)
先不管这个常数,然后裂开变成\(\sum\limits_{i=0}^{n-1}{f(i)}-\sum\limits_{i=0}^{n-1}{(-1)^{g(n)}\times f(i)}\)
前面那个就是自然数幂和显然拉插即可,时间复杂度\(O(m^2)\)
后面那个先展开,变成\(\sum\limits_{i=0}^{n-1}{(-1)^{g(n)}\sum\limits_{j=0}^{m-1}{i^j\times a_j}}\)
交换求和顺序就变成\(\sum\limits_{j=0}^{m-1}{a_j\sum\limits_{i=0}^{n-1}{(-1)^{g(n)}i^j}}\)
只要后面那个能快速求解就可以做到很快了。
考虑对其做类似于数位\(dp\)的二进制拆分。
设\(f(x,t,k)=\sum\limits_{i=x}^{2^t+x-1}{(-1)^{g(i)}i^k}=\sum\limits_{i=0}^{2^t-1}{(-1)^{g(i+x)}(i+x)^k}\)
然后因为这个是二进制拆分,所以可以化成\(g(x)\sum\limits_{i=0}^{2^t-1}{(-1)^{g(i)}(i+x)^k}\)
用二项式定理展开得到\(g(x)\sum\limits_{i=0}^{2^t-1}{(-1)^{g(i)}\sum\limits_{j=0}^{k}{C^{k}_{j}\times i^j\times x^{k-j}}}\)
交换求和顺序得到\(g(x)\sum\limits_{j=0}^{k}{C^{k}_{j}\times x^{k-j}\sum\limits_{i=0}^{2^t-1}{(-1)^{g(i)}i^j}}=g(x)\sum\limits_{j=0}^{k}{C^{k}_{j}\times x^{k-j}\times f(0,t,j)}\)
然后显然\(f(0,t,k)=f(0,t-1,k)+f(2^{t-1},t-1,k)\)于是就可以做了。这样是\(O(nm^2)\)的。
然后稍微归纳一下可以得到当\(t>k\)时这个东西是\(0\),然后就可以\(O(m^3+n)\)了。
code:
#include<cstdio>
#include<cstring>
#define I inline
#define ll long long
#define N 500039
#define K 539
#define re register
#define mod 1000000007
#define max(a,b) ((a)>(b)?(a):(b))
using namespace std;
int n,m,k,z,cnt,s[N];ll b[K],x[K],y[K],tot,pus,ans,now,c[K][K],q[N],g[K][K],po[N];char a[N];
I ll mpow(ll x,int y=mod-2){ll ans=1;while(y) (y&1)&&(ans=ans*x%mod),y>>=1,x=x*x%mod;return ans;}
I ll calc(ll x){ll ans=0,now=1;for(int i=0;i<m;i++) ans+=now*b[i]%mod,now=now*x%mod;return ans%mod;}
I ll lglr(ll n){
re int i,j;re ll xs,ys,ans=0;
for(i=1;i<=m+1;i++){
for(xs=ys=1,j=1;j<=m+1;j++){
if(i==j) continue;
xs=xs*(n-x[j]+mod)%mod,ys=ys*(x[i]-x[j]+mod)%mod;
}
ans+=xs*mpow(ys)%mod*y[i]%mod;
}
return ans%mod;
}
I ll getf(int x,int y){
if(x>y)return 0;if(!x) return y==0;
if(~g[x][y]) return g[x][y];re ll ans=getf(x-1,y),now=1;re int i;
for(i=y;~i;i--)ans-=c[y][i]*now%mod*getf(x-1,i)%mod,now=now*po[x-1]%mod;ans=(ans%mod+mod)%mod;
/*printf("%lld %d %d\n",getf(x-1,y),x-1,y);*/return g[x][y]=ans%mod;
}
I ll get(int k){
re int i,j;ll ans=0,now,pus;
for(i=max(n-k,1);i<=n;i++) {
if(a[i]=='0') continue;
now=1;pus=0;
for(j=k;~j;j--)pus+=c[k][j]*now%mod*getf(n-i,j)%mod,now=now*q[i-1]%mod;
pus%=mod;if(s[i-1]&1)pus=mod-pus;ans+=pus;
}
return ans%mod;
}
int main(){
register int i,j;memset(g,-1,sizeof(g));po[0]=1;
// freopen("1.in","r",stdin);
scanf("%s",a+1);n=strlen(a+1);
for(i=1;i<=n;i++) po[i]=po[i-1]*2%mod,s[i]=s[i-1]+(a[i]=='1');
for(i=1;i<=n;i++) q[i]=(q[i-1]+po[n-i]*(a[i]-'0'))%mod;
scanf("%d",&m);for(i=0;i<m;i++) scanf("%lld",&b[i]);
for(i=0;i<=m;i++){c[i][0]=1;
for(j=1;j<=i;j++)c[i][j]=(c[i-1][j]+c[i-1][j-1])%mod;
}
for(i=1;i<=m+1;i++) x[i]=i-1,y[i]=(calc(i-1)+y[i-1])%mod;ans=lglr((q[n]+mod-1)%mod);
for(i=0;i<m;i++)
ans=(ans-b[i]*get(i)%mod+mod)%mod/*,printf("%d\n",get(i))*/;
printf("%lld\n",ans*mpow(2)%mod);
}