• CF623E Transforming Sequence


    CF623E Transforming Sequence

    我一开始没看到模数

    看到这题,(nle 10^{18})(kle 3 imes 10^4) 就很迷惑,不是 (n>k) 就无解的吗??

    然而事实就是这样。。。如果像我一样手写快读的注意第一个数要开 long long 读。

    看懂题目后题意迅速转化成了:选 (n) 次数,每次选一个元素 (in [1,k]) 的集合,要求至少一个元素与之前选所有元素的不同,求方案数。

    接下去文章分为两部分:分割线之前都是我自己踩的雷,分割线之后是正解

    轻松搞出一个 (nk^2) 的dp,设 (dp(n,k)) 表示取了 (n) 次数,用到了 (k) 种元素的方案数,(ans=sum_{i=n}^{k}dp(n,i))

    [dp(i,j)=sum_{l=0}^{j-1}dp(i-1,l)inom{n-l}{j-l}2^i\ dp(0,0)=1 ]

    就是枚举之前选了多少种元素,然后再到 (n) 种元素里找多出的 (k-i) 种分配位置,而且之前选的 (i) 个元素可以选或者不选。

    显然那个dp可以卷积于是变成了 (k^2log k) ,但是有一个细节,(l) 的上限是 (j-1)

    看了半天,想着后面那个 (klog k) 大概率消不掉。这个dp每次转移 (1) 太浪费了吧。诶对了,说不定可以倍增FFT。

    一眨眼 (2) 小时过去了。。。woc怎么倍增啊???

    倍增FFT必须还要有一个 (dp(a+b))(dp(a),dp(b)) 之间的转移啊,这个没法转移啊。

    自闭了,去看了眼题解。状态原来不应该这么开!或许有很多初学者会和我犯同样的错误,所以上面那一大段被写了下来。


    考虑dp,设 (dp(n,k)) 表示前 (n) 轮操作中选了 (k) 种不同的数的总方案数,但是是钦定k种的前提下,也就是说哪k种已经定了。

    所以统计答案变成了:(ans=sum_{i=n}^{k}inom{k}{i}dp(n,i))

    忽然想起之前看粉兔的博客一直不理解为啥EGF要除阶乘最后再乘回来,大概原因就是这个了吧。

    [dp(1,0)=0,dp(1,i)=1(1le ile k)\ dp(i,j)=sum_{l=0}^{j-1}dp(i-1,l)inom{j}{l}2^{l} ]

    千万注意那个上界是 (j-1) 啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊,不然会像我一样调一晚上的啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊啊

    转移方程应该很显然吧?在现在的 (j) 中值里枚举 (l) 个给之前的,每一个都可以选或者不选。

    现在这个dp很好合并了,几乎是一眼可以看出下面那个式子

    [dp(a+b,i)=sum_{j=0}^{i-1}dp(a,j)dp(b,i-j)inom{i}{j}2^{bj} ]

    在最终的 (i) 个里分配 (j) 个值给前 (a) 个,后面 (b) 个直接考虑剩下的 (i-j) 个值即可。而且每一次转移都可以选择是否选 (a) 个中的每一个值,所以转移一次就乘 (2^j) 。这个dp其实可以看做转移的合并,那么转移 (b) 次就要乘 (2^{bj})

    然后就很好倍增FFT了吧!

    [egin{cases} dp(n+1,i)=sum_{j=0}^{i-1}dp(n,j)inom{i}{j}2^j\ dp(2n,i)=sum_{j=0}^{i-1}dp(n,j)dp(n,i-j)inom{i}{j}2^{nj} end{cases} ]

    化成可以卷积的式子:

    [egin{cases} dp(n+1,i)=i!sum_{j=0}^{i-1}dp(n,j)dfrac{2^j}{j!}dfrac{1}{(i-j)!}\ dp(2n,i)=i!sum_{j=0}^{i-1}dp(n,j)dfrac{2^{nj}}{j!}dp(n,i-j)dfrac{1}{(i-j)!} end{cases} ]

    三个坑点:

    • 上界要减一!!!

    • 这个出题人不讲武德,好端端的NTT题,结果mod=1e9+7,我没闪,被偷袭了。

    • MTT精度要好,建议预处理单位根,比不预处理快了一倍(因为不预处理会被卡精度,然后WA,所以要开 long double

    //Orz cyn2006
    #include<bits/stdc++.h>
    using namespace std;
    #define fi first
    #define se second
    #define mkp(x,y) make_pair(x,y)
    #define pb(x) push_back(x)
    #define sz(v) (int)v.size()
    typedef long long LL;
    typedef double db;
    template<class T>bool ckmax(T&x,T y){return x<y?x=y,1:0;}
    template<class T>bool ckmin(T&x,T y){return x>y?x=y,1:0;}
    #define rep(i,x,y) for(int i=x,i##end=y;i<=i##end;++i)
    #define per(i,x,y) for(int i=x,i##end=y;i>=i##end;--i)
    inline int read(){
        int x=0,f=1;char ch=getchar();
        while(!isdigit(ch)){if(ch=='-')f=0;ch=getchar();}
        while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
        return f?x:-x;
    }
    const int N=30005;
    const int M=N<<2;
    const int mod=1e9+7;
    inline int qpow(int n,int k){int res=1;for(;k;k>>=1,n=1ll*n*n%mod)if(k&1)res=1ll*n*res%mod;return res;}
    int n,k,f[M],ans;
    namespace poly{
    const db pi=acos(-1.0);
    int rev[M],lg,lim;
    int fac[M],ifc[M];
    void initmath(const int&n){
    	fac[0]=1;for(int i=1;i<=n;++i)fac[i]=1ll*i*fac[i-1]%mod;
    	ifc[n]=qpow(fac[n],mod-2);for(int i=n-1;i>=0;--i)ifc[i]=1ll*ifc[i+1]*(i+1)%mod;
    }
    struct cp{
    	db x,y;
    	cp(){x=y=0;}
    	cp(db x_,db y_){x=x_,y=y_;}
    	cp operator + (const cp&t)const{return cp(x+t.x,y+t.y);}
    	cp operator - (const cp&t)const{return cp(x-t.x,y-t.y);}
    	cp operator * (const cp&t)const{return cp(x*t.x-y*t.y,x*t.y+y*t.x);}
    }w[M];
    void init_poly(const int&n){
    	for(lim=1,lg=0;lim<=n;lim<<=1,++lg);
    	for(int i=0;i<lim;++i)rev[i]=(rev[i>>1]>>1)|((i&1)<<(lg-1)),w[i]=cp(cos(2.*pi*i/lim),sin(2.*pi*i/lim));
    }
    void FFT(cp*a,int op){
        for(int i=0;i<lim;++i)if(i>rev[i])swap(a[i],a[rev[i]]);
        for(int i=1,t=lim>>1;i<lim;i<<=1,t>>=1){
    	    for(int j=0;j<lim;j+=i<<1){
    		    for(int k=0;k<i;++k){
    			    const cp X=a[j+k],Y=w[t*k]*a[i+j+k];
    			    a[j+k]=X+Y,a[i+j+k]=X-Y;
    			}
    		}
    	}
        if(!op)for(int i=0;i<lim;++i)a[i].x/=lim;
    }
    void MTT(int*f,int*g,int*ans){	
    	static cp A[M],B[M],C[M],D[M],E[M],F[M],G[M];
    	for(int i=0;i<lim;++i)
    		A[i]=cp(f[i]&65535,0),B[i]=cp(f[i]>>16,0),
    		C[i]=cp(g[i]&65535,0),D[i]=cp(g[i]>>16,0);
    	FFT(A,1),FFT(B,1),FFT(C,1),FFT(D,1);
    	for(int i=0;i<lim;++i)
    		E[i]=A[i]*C[i],F[i]=A[i]*D[i]+B[i]*C[i],G[i]=B[i]*D[i],w[i].y*=-1;
    	FFT(E,0),FFT(F,0),FFT(G,0);
    	for(int i=0;i<lim;++i)
    		ans[i]=LL(G[i].x+0.5)%mod,
    		ans[i]=((65536ll*ans[i]%mod)+LL(F[i].x+0.5)%mod)%mod,
    		ans[i]=((65536ll*ans[i]%mod)+LL(E[i].x+0.5)%mod)%mod,
    		w[i].y*=-1;
    }
    #define clr(a,n) memset(a,0,sizeof(int)*(n))
    #define cpy(a,b) memcpy(a,b,sizeof(int)*(n))
    void shift(const int&n,const int&len){
    	static int g[M],h[M];
    	clr(g,lim),clr(h,lim);
    	for(int i=0,bas=qpow(2,len),j=1;i<n;++i,j=1ll*j*bas%mod)g[i]=1ll*f[i]*j%mod*ifc[i]%mod;
    	for(int i=1;i<=n;++i)h[i]=1ll*f[i]*ifc[i]%mod;
    	MTT(g,h,f);
    	for(int i=0;i<=n;++i)f[i]=1ll*f[i]*fac[i]%mod;
    	clr(f+n+1,lim-n);
    }
    void setbit(const int&n){
    	static int g[M],h[M];
    	clr(g,lim),clr(h,lim);
    	for(int i=0,j=1;i<n;++i,(j<<=1)%=mod)g[i]=1ll*f[i]*j%mod*ifc[i]%mod;
    	for(int i=1;i<=n;++i)h[i]=ifc[i];
    	MTT(g,h,f);
    	for(int i=0;i<=n;++i)f[i]=1ll*f[i]*fac[i]%mod;
    	clr(f+n+1,lim-n);
    }
    }
    using poly::fac;
    using poly::ifc;
    signed main(){
    	LL whatsthis;scanf("%lld%d",&whatsthis,&k);
    	if(whatsthis>k)return puts("0"),0;
    	n=whatsthis;
    	f[0]=0;rep(i,1,k)f[i]=1;
    	poly::init_poly(k<<1),poly::initmath(k);
    	for(int i=log2(n)-1,len=1;i>=0;--i){
    		poly::shift(k,len),len<<=1;
    		if(n>>i&1)poly::setbit(k),++len;
    	}
    	for(int i=n;i<=k;++i)ans=(ans+1ll*f[i]*fac[k]%mod*ifc[i]%mod*ifc[k-i]%mod)%mod;
    	printf("%d
    ",ans);
    	return 0;
    } 
    
  • 相关阅读:
    paip.云计算以及分布式计算的区别
    paip.索引的种类以及实现attilax 总结
    paip.分布式应用系统java c#.net php的建设方案
    paip.提升性能--多核编程中的java .net php c++最佳实践 v2.0 cah
    paip.中文 分词 ---paoding 3.1 的使用
    paip.2013年技术趋势以及热点 v2.0 cae
    paip.为什么使用多线程的原因.
    paip.提升性能--多核cpu中的java/.net/php/c++编程
    paip.重装系统需要备份的资料总结..v2.0 cad
    paip.禁用IKAnalyzer 的默认词库.仅仅使用自定义词库.
  • 原文地址:https://www.cnblogs.com/zzctommy/p/14194587.html
Copyright © 2020-2023  润新知