CF623E Transforming Sequence
Transforming Sequence
给定\(n,k\)。
让你构造序列\(a(0<a_i<2^k)\),满足\(b_i(b_i=a_1\ \textrm{or}\ a_2\ \textrm{or}\ \cdots\ \textrm{or}\ a_i)\)严格单调递增。(or为按位或)
问你方案总数。对\(10^9+7\)取模。
\(n\leq 10^{18},k\leq 30000\)
zhouzhendong的题解
考虑有\(k\)个数位,要保证单调递增,由于是or运算,所以值为\(1\)的数位不管怎样或都不变了,所以每多一个\(a_i\)至少要多让一个数位变成\(1\)。
因为只有\(k\)个数位,所以\(a\)的元素个数最多有\(n\)个,即\(n\leq k\)。当\(n>k\)时就是无解,输出0。
大力DP
设\(dp_{i,j}\)表示前\(i\)个数占用了\(j\)个二进制位的方案数。注意这里不考虑占用的是哪几个二进制位,在最后乘以一个\(\binom{k}{n}\)就行了。
则不难列出转移方程:
注意\(j\)从\(0\)开始枚举其实和从\(i\)开始是等价的。
因为原本就是\(1\)的数位,新增的数的那一位不管是\(0\)还是\(1\)都等价,所以要乘\(2^j\)。组合数意义很明显。
但是这样太慢了,要TLE。
DP优化
我们发现这个DP可以成段的转移。
设原本有\(x\)个数,现在一下子加入\(y\)个数,写出转移方程:
其中,由于要加入\(y\)个数字,每一个数字都在原有的\(j\)个二进制位\(1\)中贡献了\(2^j\)的系数(和之前同理),所以总的系数贡献就是\((2^j)^y=2^{jy}\)了。
我们发现这个是个多项式卷积的形式,所以可以直接FFT优化。注意这个FFT要拆系数,不然要爆long long
。
这样的话我们可以运用倍增的套路来解决整个运算过程。
最终的答案就是\(\sum_{i=0}^{k}\binom{k}{i}dp_{n,i}\)。
注意,我代码里面把\(k\)写成了\(m\)。时间复杂度\(O(k\log^2 k)\)。
练习了一下拆系数,没有想象中的那么难,简单来说就是拆一个\(2^{15}\)出来。总共要做7次FFT,感觉三模数就是个废物。
co double pi=acos(-1);
struct node {double x,y;};
il node operator+(co node&a,co node&b){
return (node){a.x+b.x,a.y+b.y};
}
il node operator-(co node&a,co node&b){
return (node){a.x-b.x,a.y-b.y};
}
il node operator*(co node&a,co node&b){
return (node){a.x*b.x-a.y*b.y,a.x*b.y+a.y*b.x};
}
co int mod=1e9+7;
il int add(int a,int b){
return (a+=b)>=mod?a-mod:a;
}
il int mul(int a,int b){
return (LL)a*b%mod;
}
il int fpow(int a,int b){
int ans=1;
for(;b;b>>=1,a=mul(a,a))
if(b&1) ans=mul(ans,a);
return ans;
}
co int N=1<<17;
int n,m;
int fac[N],ifac[N];
node w[N],a1[N],a2[N],b1[N],b2[N],A[N],B[N],C[N];
int lim,len,rev[N];
void trans(node a[]){
for(int i=0;i<lim;++i)
if(i<rev[i]) swap(a[i],a[rev[i]]);
for(int step=1;step<lim;step<<=1){
int quot=lim/(step<<1);
for(int i=0;i<lim;i+=step<<1){
int j=i+step;
for(int k=0;k<step;++k){
node t=w[quot*k]*a[j+k];
a[j+k]=a[i+k]-t,a[i+k]=a[i+k]+t;
}
}
}
}
void mul_to(int a[],int b[],int c[]){
for(int i=0;i<lim;++i){
a1[i]=(node){a[i]>>15,0},a2[i]=(node){a[i]&0x7fff,0};
b1[i]=(node){b[i]>>15,0},b2[i]=(node){b[i]&0x7fff,0};
}
trans(a1),trans(a2),trans(b1),trans(b2);
for(int i=0;i<lim;++i){
A[i]=a1[i]*b1[i];
B[i]=a1[i]*b2[i]+a2[i]*b1[i];
C[i]=a2[i]*b2[i];
w[i].y=-w[i].y;
}
trans(A),trans(B),trans(C);
for(int i=0;i<lim;++i){
A[i].x=round(A[i].x/lim);
B[i].x=round(B[i].x/lim);
C[i].x=round(C[i].x/lim);
c[i]=add(mul((LL)A[i].x%mod,(1LL<<30)%mod),add(mul((LL)B[i].x%mod,(1LL<<15)%mod),(LL)C[i].x%mod));
w[i].y=-w[i].y;
}
}
int f[N],g[N],D[N],E[N];
void merge(int a[],int b[],int c[],int&x,int&y){
for(int i=0;i<lim;++i) D[i]=E[i]=0;
for(int i=0;i<=m;++i){
D[i]=mul(a[i],mul(ifac[i],fpow(2,(LL)y*i%(mod-1))));
E[i]=mul(b[i],ifac[i]);
}
mul_to(D,E,c);
for(int i=0;i<=m;++i) c[i]=mul(c[i],fac[i]);
for(int i=m+1;i<lim;++i) c[i]=0;
x+=y;
}
int main(){
LL nn=read<LL>();read(m);
if(nn>m) return puts("0"),0;
else n=nn;
fac[0]=1;
for(int i=1;i<=m;++i) fac[i]=mul(fac[i-1],i);
ifac[m]=fpow(fac[m],mod-2);
for(int i=m-1;i>=0;--i) ifac[i]=mul(ifac[i+1],i+1);
len=ceil(log2((m<<1)+1)),lim=1<<len;
for(int i=0;i<lim;++i){
rev[i]=rev[i>>1]>>1|(i&1)<<(len-1);
w[i]=(node){cos(i*2*pi/lim),sin(i*2*pi/lim)};
}
f[0]=1;
for(int i=1;i<=m;++i) g[i]=1; // g[0]=1 -> epsilon
for(int x=0,y=1;y<=n;merge(g,g,g,y,y))
if(n&y) merge(f,g,f,x,y);
int ans=0;
for(int i=0;i<=m;++i)
ans=add(ans,mul(f[i],mul(fac[m],mul(ifac[i],ifac[m-i]))));
printf("%d\n",ans);
return 0;
}
这个Warning
narrowing conversion of '……' from 'int' to 'double' inside { } is ill-formed in C++11 [-Wnarrowing]
神烦,C++11跟我有什么关系。