zoukankan      html  css  js  c++  java
  • [NOI Online 2021 Ⅰ 提高组题解] 愤怒的小N

    题意简述

    数列 ({b_n}) 构造如下:(即 Thue-Morse 数列,OEIS A010060

    • (b_0=0)
    • (forall iin [2^n,2^{n+1}),b_i=1-b_{i-2^n})

    输入 (n) 和一个次数为 (m-1) 次的多项式 (f(x)=sumlimits_{i=0}^{m-1}a_ix^i) ,求 (sumlimits_{i=0}^{n-1}b_if(i)) ,答案对 (10^9+7) 取模。

    (0le log_2 nle 5cdot 10^5 , 1le mle 500 , 0le a_i< 10^9+7 , a_{m-1} eq 0)

    算法分析

    我们首先尝试找到 ({b_n}) 的通项,方便我们下面的求解。

    由构造过程,不难发现,我们在 (x) 的最高位前添上一个 (1) (设为 (y) ),则 (b_{y}=b_{x}mathrm{xor}1) ,这个过程中最明显的变化就是二进制中 (1) 的个数 (+1) ,因此我们有如下结论:

    [b_{n}=mathrm{popcount}(n) pmod 2 ]

    (mathrm{popcount}(n)) 即为 (n) 的二进制表示下有多少个 (1)

    但这个(pmod 2) 并不好处理,我们转化成乘积,即:

    [b_{n}=frac{1}{2}left(1-(-1)^{mathrm{popcount}(n)} ight) ]

    因此答案如下:

    [ans=frac{1}{2}sum_{i=0}^{n-1}left(1-(-1)^{mathrm{popcount}(i)} ight)f(i) ]

    稍作化简:

    [ans=frac{1}{2}sum_{i=0}^{n-1}f(i)-frac{1}{2}sum_{i=0}^{n-1}(-1)^{mathrm{popcount}(i)}f(i) ]

    由于 (f(x)) 是一个 (m-1) 次多项式,因此可以归纳证明 (f(x)) 的前缀和是一个 (m) 次多项式,用拉格朗日插值即可求出,时间复杂度 (O(m^2))

    现在我们只看后面一部分,即:

    [sum_{i=0}^{n-1}(-1)^{mathrm{popcount}(i)}f(i) ]

    (f(i)) 展开,交换求和顺序:

    [sum_{k=0}^{m-1}a_ksum_{i=0}^{n-1}(-1)^{mathrm{popcount}(i)}i^k ]

    我们只要能求出 (sumlimits_{i=0}^{n-1}(-1)^{mathrm{popcount}(i)}i^k),即可 (mathcal{O}(m)) 计算。

    考虑类似于 数位dp 的做法,([0,n)) 这个区间拆解为若干个形如 ([x,x+2^t)(x>2^t)) 的区间,这样我们所有的区间长度都是 (2) 的整次幂,方便我们计算。

    举个例子,(n=left(10101101 ight)_2),我们拆解为如下几个区间:

    • (left[left(00000000 ight)_2,left(10000000 ight)_2 ight))
    • (left[left(10000000 ight)_2,left(10100000 ight)_2 ight))
    • (left[left(10100000 ight)_2,left(10101000 ight)_2 ight))
    • (left[left(10101000 ight)_2,left(10101100 ight)_2 ight))
    • (left[left(10101100 ight)_2,left(10101101 ight)_2 ight))

    接下来我们的工作就是要计算 (sumlimits_{i=x}^{x+2^t-1}(-1)^{mathrm{popcount}(i)}i^k),不妨记此为 (f(x,t,k)),则:

    [f(x,t,k)=sum_{i=0}^{2^t-1}(-1)^{mathrm{popcount}(i+x)}(i+x)^k ]

    我们划分区间的优势现在就体现出来了,因为 (x>2^t) ,所以 (mathrm{lowbit}(x)>mathrm{highbit}(2^t)) ,因此 (mathrm{popcount}) 我们可以独立计算,即:

    [f(x,t,k)=(-1)^{mathrm{popcount}(x)}sum_{i=0}^{2^t-1}(-1)^{mathrm{popcount}(i)}(i+x)^k ]

    后面的 ((i+x)^k) 用二项式定理展开,即:

    [f(x,t,k)=(-1)^{mathrm{popcount}(x)}sum_{i=0}^{2^t-1}(-1)^{mathrm{popcount}(i)}sum_{j=0}^kdbinom{k}{j}i^jx^{k-j} ]

    继续交换求和顺序,并独立出 (x) ,即:

    [f(x,t,k)=(-1)^{mathrm{popcount}(x)}sum_{j=0}^kdbinom{k}{j}x^{k-j}sum_{i=0}^{2^t-1}(-1)^{mathrm{popcount}(i)}i^j ]

    子问题出现了,即:

    [f(x,t,k)=(-1)^{mathrm{popcount}(x)}sum_{j=0}^kdbinom{k}{j}x^{k-j}f(0,t,j) ]

    (f(0,t,j)) 并不能由我们上面的式子计算出,考虑 ([0,2^t)=[0,2^{t-1})cup[2^{t-1},2^t)),而这种区间的划分仍然满足上面的条件((x>2^t)),因此:

    [f(0,t,k)=f(0,t-1,k)+f(2^{t-1},t-1,k) ]

    综上,我们可以在 (mathcal{O}(m)) 的时间复杂度实现 (f(x,t,k)) 的转移,后面两维状态数是 (mathcal{O}(m^2)) 的,总共有 (mathcal{O}(log_2 n+m))个可能的(x),因此因此总复杂度为 (mathcal{O}((log_2n+m)m^2)) 的,可以通过 (60\%) 的数据。

    通过打表,发现 (f(x,t,k))(x>2^k) 或者 (t>k) 时都是 (0) 。这个也是可以归纳证明的,(但我不会证),此处略去。

    因此所有 (>m)(x) 都没有用,因此实际上只有 (mathcal{O}(m)) 个可能的不同的 (x) ,复杂度为 (mathcal{O}(log_2n+m^3)) ,可以通过。

    代码实现

    #include<bits/stdc++.h>
    using namespace std;
    #define maxn 1000005
    #define maxm 2000005
    #define inf 0x3f3f3f3f
    #define LL long long
    #define ull unsigned long long
    #define db double
    #define ldb long double
    #define mod 1000000007
    #define eps 1e-9
    #define local
    void file(string s){freopen((s+".in").c_str(),"r",stdin);freopen((s+".out").c_str(),"w",stdout);}
    template <typename Tp> void read(Tp &x){
    	int fh=1;char c=getchar();x=0;
    	while(c>'9'||c<'0'){if(c=='-'){fh=-1;}c=getchar();}
    	while(c>='0'&&c<='9'){x=(x<<1)+(x<<3)+(c&15);c=getchar();}x*=fh;
    }
    int ksm(int b,int p=mod-2,int Mod=mod){int ret=1;while(p){if(p&1)ret=1ll*ret*b%Mod;b=1ll*b*b%Mod;p>>=1;}return ret;}
    
    int iv2=((mod+1)/2);
    int n,m;
    char ss[maxn];
    int a[maxn];
    struct Larg{//拉格朗日插值求前缀和
    	int calc_f(int x){
    		int ret=0;
    		for(int i=m-1;i>=0;--i){
    			ret=(1ll*ret*x+a[i])%mod;
    		}
    		return ret;
    	}
    	int frac[505],frinv[505],xi[505],yi[505],syi[505],N;
    	int pv[505],sf[505];
    	void prepr(){
    		N=m+1;
    		frac[0]=1;for(int i=1;i<=N;++i)frac[i]=1ll*frac[i-1]*i%mod;
    		frinv[N]=ksm(frac[N]);for(int i=N;i;--i)frinv[i-1]=1ll*frinv[i]*i%mod;
    		yi[0]=calc_f(0);
    		for(int i=1;i<=N;++i){
    			xi[i]=i;yi[i]=(yi[i-1]+calc_f(i))%mod;
    			int prv=frinv[i-1],suf=frinv[N-i];
    			if((i^N)&1)suf=(mod-suf);
    			syi[i]=1ll*yi[i]*prv%mod*suf%mod;
    		}
    	}
    	int calc_sf(int x){
    		x%=mod;
    		pv[0]=sf[N+1]=1;
    		for(int i=1;i<=N;++i)pv[i]=1ll*pv[i-1]*(x-xi[i])%mod;
    		for(int i=N;i;--i)sf[i]=1ll*sf[i+1]*(x-xi[i])%mod;
    		int ret=0;
    		for(int i=1;i<=N;++i){
    			ret=(ret+1ll*syi[i]*pv[i-1]%mod*sf[i+1]%mod)%mod;
    		}
    		return (ret+mod)%mod;
    	}
    }La;
    
    int ans1,ans2;
    int C[505][505];
    int p2[maxn];
    void prep(){
    	for(int i=0;i<=m;++i){
    		C[i][0]=C[i][i]=1;
    		for(int j=1;j<i;++j){
    			C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
    		}
    	}
    	p2[0]=1;
    	for(int i=1;i<=n;++i)p2[i]=2ll*p2[i-1]%mod;
    }
    int ff[505][505];
    
    int calc(int tn,int k){//计算 f(0,tn,k)
    	if(tn>k)return 0;
    	if(tn==0)return k==0;
    	if(ff[tn][k]!=-1)return ff[tn][k];
    	int ret=calc(tn-1,k);
    	int X=p2[tn-1];
    	int px=1;
    	for(int j=k;j>=0;--j,px=1ll*px*X%mod){
    		ret=(ret+mod-1ll*C[k][j]*px%mod*calc(tn-1,j)%mod)%mod;
    	}
    	return ff[tn][k]=ret;
    }
    
    void solve(){
    	int tnm=0,tppc=0;
    	for(int i=0;i<n;++i){
    		int cnm=(2ll*tnm+ss[i]-'0')%mod,cppc=(tppc^(ss[i]-'0'));
    		if(n-i-1<=m){//无用的x直接剪枝减掉
    			if(ss[i]=='1'){//划分区间
    				int X=1ll*tnm*p2[n-i]%mod;
    				for(int k=0;k<m;++k){
    					int sm=0;
    					int px=1;
    					for(int j=k;j>=0;--j,px=1ll*px*X%mod){
    						sm=(sm+1ll*C[k][j]*px%mod*calc(n-i-1,j)%mod)%mod;
    					}
    					if(tppc)sm=(mod-sm);
    					ans2=(ans2+1ll*a[k]*sm%mod)%mod;
    				}
    			}	
    		}
    		
    		tnm=cnm,tppc=cppc;
    	}
    }
    
    signed main(){
    	#ifndef local
    		file("angry");
    	#endif
    	memset(ff,-1,sizeof(ff));
    	scanf("%s",ss);n=strlen(ss);
    	read(m);
    	for(int i=0;i<m;++i)read(a[i]);
    	La.prepr();prep();
    	int nm=0;
    	for(int i=0;i<n;++i)nm=(2ll*nm+ss[i]-'0')%mod;
    	nm=(nm+mod-1)%mod;
    	ans1=La.calc_sf(nm);
    	solve();
    	int ans=(ans1+mod-ans2)%mod;
    	ans=1ll*ans*iv2%mod;
    	printf("%d
    ",ans);
    	fclose(stdin);
    	fclose(stdout);
    	return 0;
    }
    
  • 相关阅读:
    浏览器兼容性优化
    js操作table(增加行,删除行,上移,下移,复制行)
    褚时健传读后感
    新鞋
    八达岭奥莱
    springMVC 多方法controller
    springMVC入门配置及helloworld实例
    springMVC源码下载地址
    spring3mvc与struts2比较
    hql语句拼接的替换方式
  • 原文地址:https://www.cnblogs.com/ZigZagKmp/p/14586444.html
Copyright © 2011-2022 走看看