CF623E Transforming Sequence

Posted autoint

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了CF623E Transforming Sequence相关的知识,希望对你有一定的参考价值。

Transforming Sequence

给定\\(n,k\\)

让你构造序列\\(a(0<a_i<2^k)\\),满足\\(b_i(b_i=a_1\\ \\textrmor\\ a_2\\ \\textrmor\\ \\cdots\\ \\textrmor\\ 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\\)个二进制位的方案数。注意这里不考虑占用的是哪几个二进制位,在最后乘以一个\\(\\binomkn\\)就行了。

则不难列出转移方程:
\\[ dp_i+1,x=\\sum_j=0^x-12^j\\binomxjdp_i,j \\]
注意\\(j\\)\\(0\\)开始枚举其实和从\\(i\\)开始是等价的。

因为原本就是\\(1\\)的数位,新增的数的那一位不管是\\(0\\)还是\\(1\\)都等价,所以要乘\\(2^j\\)。组合数意义很明显。

但是这样太慢了,要TLE。

DP优化

我们发现这个DP可以成段的转移。

设原本有\\(x\\)个数,现在一下子加入\\(y\\)个数,写出转移方程:
\\[ dp_x+y,i=\\sum_j=0^i2^jy\\binomijdp_x,j\\cdot dp_y,i-j \\]
其中,由于要加入\\(y\\)个数字,每一个数字都在原有的\\(j\\)个二进制位\\(1\\)中贡献了\\(2^j\\)的系数(和之前同理),所以总的系数贡献就是\\((2^j)^y=2^jy\\)了。

我们发现这个是个多项式卷积的形式,所以可以直接FFT优化。注意这个FFT要拆系数,不然要爆long long

这样的话我们可以运用倍增的套路来解决整个运算过程。

最终的答案就是\\(\\sum_i=0^k\\binomkidp_n,i\\)

注意,我代码里面把\\(k\\)写成了\\(m\\)。时间复杂度\\(O(k\\log^2 k)\\)


练习了一下拆系数,没有想象中的那么难,简单来说就是拆一个\\(2^15\\)出来。总共要做6次FFT,感觉三模数就是个废物。

#include<bits/stdc++.h>
#define co const
#define il inline
template<class T> T read()
    T x=0,w=1;char c=getchar();
    for(;!isdigit(c);c=getchar())if(c=='-') w=-w;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*w;

template<class T>il T read(T&x)
    return x=read<T>();

using namespace std;
typedef long long LL;

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跟我有什么关系。

以上是关于CF623E Transforming Sequence的主要内容,如果未能解决你的问题,请参考以下文章

CF623E Transforming Sequence

codeforces 623E Transforming Sequence

CodeForces - 623E(倍增+ntt)

CodeForces - 623E(倍增+ntt)

5 Ways AI is Transforming the Finance Industry

[Immutable.js] Transforming Immutable Data with Reduce