「CF623E-Transforming Sequence」题解
E. Transforming Sequence
sol
考虑设计 DP,令 \(f_{i,j}\) 表示长为 \(i\) 的合法序列或和为 \(2^j-1\) 的方案数。
先不说转移,这个状态一眼看上去难以转化为答案,但实际上并不困难。答案可以表示为:
考虑在 \(k\) 位中选出 \(j\) 位,其余位显然可以不考虑,此时这种情况的方案等价于凑出 \(2^j-1\) 的方案。
然后考虑转移,我们先给出递推式:
接下来解释这个式子,想凑出 \(k\) 位 \(1\),可以枚举上一步凑出了多少 \(1\),\(\binom{k}{i}\) 即为上一步凑出的 \(1\) 在当前结果中的位置。但我们需要考虑数列中两个数存在一些位均为 \(1\) 的重合情况,不难发现对于这一步的新数而言,上一步已经凑出的 \(1\) 的位置,其可以任意为 \(1\) 或 \(0\),共 \(2^i\) 种情况。由于严格单调递增,这里 \(i\) 的枚举上限应为 \(k-1\)。严格上来说下限应为 \(n-1\),但考虑不合法情况 \(f_{n-1,i}=0\),不会造成影响。
但这样一层层枚举显然会 T 爆,我们发现其实不用一位一位加入新点,而可以把一个序列视作两个序列的拼接,于是可得:
其余定义是类似的,我们只额外讲一下最后 \(2^{mi}\) 的含义。假令长 \(m\) 的序列为新拼接入的序列,那么对于其每个元素,\(i\) 个原先为 \(1\) 的位均可自由选择,于是就有了 \(2^{mi}\) 种情况。这里的上下界均宽松,因为不合法状态 \(f=0\) 所以没有影响。
到这里其实已经很简单了,上面很显然是一个卷积状物,下面详细讲解一下卷积部分。
展开可得:
为了方便,我们转为维护 \(f'_{i,j}=\frac{f_{i,j}}{j!}\),同时额外维护 \(\frac{f_{n,i}2^{mi}}{i!}\),显然可以在每次转移前得到 \(m\) 的值。
然后就很简单了,倍增即可。卷积部分需要使用 MTT。
code
namespace MTT{
int P=1e9+7;
struct clx{
flt x,y;
clx(flt a=0,flt b=0){x=a,y=b;}
inline clx operator+=(const clx &b){return x+=b.x,y+=b.y,*this;}
friend inline clx operator+(clx a,clx b){return a+=b;}
inline clx operator-=(const clx &b){return x-=b.x,y-=b.y,*this;}
friend inline clx operator-(clx a,clx b){return a-=b;}
inline clx operator*=(const clx &b){return *this=clx(x*b.x-y*b.y,x*b.y+y*b.x);}
friend inline clx operator*(clx a,clx b){return a*=b;}
inline clx operator!(){return clx(x,-y);}
};
typedef vec<ll> poly;
typedef vec<clx> Poly;
const flt Pi=acos(-1);
vec<int> Rt;
inline void fft(int lim,Poly &a,int type){
repl(i,0,lim)if(i<Rt[i])swap(a[i],a[Rt[i]]);
for(int mid=1;mid<lim;mid<<=1){
clx w1(cos(Pi/mid),type*sin(Pi/mid));
for(int j=0;j<lim;j+=(mid<<1)){
clx w(1,0);
repl(k,0,mid){
clx x=a[j+k],y=w*a[j+mid+k];
a[j+k]=x+y;
a[j+mid+k]=x-y;
w=w*w1;
}
}
}
}
inline void operator*=(poly &x,poly y){
int n=x.size(),m=y.size(),M=ceil(sqrt(P));
int lim=1,l=0,len=n+m-1;
while(lim<len)lim<<=1,l++;
Rt.resize(lim);
repl(i,0,lim)Rt[i]=(Rt[i>>1]>>1)|((i&1)<<(l-1));
Poly p(lim),q(lim),s(lim),t(lim);
x.resize(lim),y.resize(lim);
repl(i,0,lim)p[i]=clx(x[i]/M,x[i]%M),q[i]=clx(y[i]/M,y[i]%M);
fft(lim,p,1);fft(lim,q,1);
repl(i,0,lim){
clx ka=(p[i]+!p[i?lim-i:i])*clx(0.5,0);
clx ba=(p[i]-!p[i?lim-i:i])*clx(0,-0.5);
clx kb=(q[i]+!q[i?lim-i:i])*clx(0.5,0);
clx bb=(q[i]-!q[i?lim-i:i])*clx(0,-0.5);
s[i]=ka*kb+ka*bb*clx(0,1);
t[i]=ba*kb+ba*bb*clx(0,1);
}
fft(lim,s,-1);fft(lim,t,-1);
repl(i,0,lim){
ll a=(ll)(s[i].x/lim+0.5)%P;
ll b=(ll)(s[i].y/lim+0.5)%P;
ll c=(ll)(t[i].x/lim+0.5)%P;
ll d=(ll)(t[i].y/lim+0.5)%P;
x[i]=a*M*M%P+(b+c)*M%P+d,x[i]%=P;
}
x.resize(len);
for(auto &i:x)i=(i+P)%P;
}
inline void operator+=(poly &a,poly b){
if(a.size()<b.size())a.resize(b.size());
repl(i,0,b.size())a[i]=a[i]+b[i],a[i]%=P;
}
inline void operator-=(poly &a,poly b){
if(a.size()<b.size())a.resize(b.size());
repl(i,0,b.size())a[i]=a[i]-b[i]+P,a[i]%=P;
}
poly operator*(poly a,poly b){return a*=b,a;}
poly operator+(poly a,poly b){return a+=b,a;}
poly operator-(poly a,poly b){return a-=b,a;}
}using namespace MTT;
const int T=65,N=3e4+5;
ll n;int k;
poly f[T],g[T],ans;
mint jc[N],iv[N];
inline void Main(){
read(n,k);
f[0].resize(k+1);g[0].resize(k+1);
rep(j,1,k)f[0][j]=iv[j].x,g[0][j]=(f[0][j]*((mint)2^j)).x;
int tt=__lg(n);
rep(t,1,tt){
f[t]=g[t-1]*f[t-1];f[t].resize(k+1);
g[t].resize(k+1);rep(j,1,k)g[t][j]=(f[t][j]*((mint)2^(1ll<<t)^j)).x;
}
ans.resize(k+1);
ans[0]=1;
rep(t,0,tt)if(n&1ll<<t){
rep(j,1,k)ans[j]*=((mint)2^(1ll<<t)^j).x,ans[j]%=P;
ans*=f[t];ans.resize(k+1);
}
mint res=0;
rep(i,1,k)res+=ans[i]*jc[i]*jc[k]*iv[i]*iv[k-i];
put(res);
}

浙公网安备 33010602011771号