zoukankan      html  css  js  c++  java
  • 几何

     题目大意
    定义一个$S-$四面体表示六条边由$S$根不同的木棍组成,定义一种染色方法合法当且仅当至少有$S$根木棍被染色且与每个顶点相邻的三根木棍中至多有一根被染色,求有$N$个$S=1,2...N$四面体,求至少染$K$个的方案数。

    题解

    单独考虑$S=1$四面体,染它有$9$中方案,否则将与每个顶点相邻的$12$条边单独拿出来计算。

    考虑其余四面体被染色的方案数:他们中有$k$个顶点$(k=0,1,2,3,4)$相邻的木棍三条中有一条被染色,方案是$C_4^k imes 3^k$,剩余的边则有$sumlimits_{i=S-k}^{6S-12}C_{6S-12}^{i}$,用乘法原理乘一下即可。

    由于$k$很小,可以只求$sumlimits_{i=S}^{6S-12}C_{6S-12}^{i}$,其余加上组合数即可。而这个式子是杨辉三角上的某一行的一个后缀,所以是可以递推的,考虑上一行答案对下一行答案的贡献,只需要乘二再加上上一行后缀左侧的那个组合数即可。

    我们现在已经解决了每一个$S$的方案数,接下来就是求染出至少$K$个的方案数,也只需要递推,设$G_x$表示染$x$个的方案数$V$表示当染前四边形的方案数,$G_x=G_x+V imes G_{x-1}$。这是$N$个简单最高次项次数是一次的多项式的卷积,用分治$FFT$解决即可。

    有两个细节,由于模数只有$10^5+3$,所以算组合数要用$lucas$定理。并且,$FFT$中每一位可能达到$10^{15}$级别,精度可能会爆炸,所以要优化或者使用$longspace double$。

    #include<algorithm>
    #include<iostream>
    #include<cstring>
    #include<cstdio>
    #include<cmath>
    #define LL long long
    #define LD long double
    #define mod 100003
    #define M 100020
    using namespace std;
    int read(){
    	int nm=0,fh=1; int cw=getchar();
    	for(;!isdigit(cw);cw=getchar()) if(cw=='-') fh=-fh;
    	for(;isdigit(cw);cw=getchar()) nm=nm*10+(cw-'0');
    	return nm*fh;
    }
    const int P[]={1,12,54,108,81};
    const LD PI=3.141592653589793238462643383;
    int mul(int x,int y){return (LL)x*(LL)y%mod;}
    int add(int x,int y){return (x+y)>=mod?x+y-mod:x+y;}
    int mus(int x,int y){return (x-y)<0?x-y+mod:x-y;}
    void upd(int &x,int y){x=add(x,y);}
    int qpow(int x,int sq){
    	int res=1;
    	for(;sq;sq>>=1,x=mul(x,x)) if(sq&1) res=mul(res,x);
    	return res;
    }
    int n,m,fac[mod],ifac[mod],G[M<<2],lg[M<<2]; 
    int C(int tot,int tk){if(tot<0||tk<0||tot<tk)return 0;return mul(fac[tot],mul(ifac[tot-tk],ifac[tk]));}
    int lucas(int tot,int tk){if(!tk) return 1;return mul(C(tot%mod,tk%mod),lucas(tot/mod,tk/mod));}
    int rev[M<<2],F[M<<2],S[M];
    struct comp{
    	LD r,d; comp(){r=d=0.0;}
    	comp(LD _r,LD _d){r=_r,d=_d;}
    	comp operator +(const comp&k)const{return comp(r+k.r,d+k.d);}
    	comp operator -(const comp&k)const{return comp(r-k.r,d-k.d);}
    	comp operator *(const comp&k)const{return comp(r*k.r-d*k.d,r*k.d+d*k.r);}
    }A[M<<2],B[M<<2];
    void FFT(comp *x,int len,LD kd){
    	for(int i=1;i<len;i++) if(i<rev[i]) swap(x[i],x[rev[i]]);
    	for(int tt=1;tt<len;tt<<=1){
    		comp unit(cos(PI*kd/(tt*1.0)),sin(PI*kd/(tt*1.0)));
    		for(int st=0;st<len;st+=(tt<<1)){
    			comp now(1.0,0.0);
    			for(int pos=st;pos<st+tt;pos++,now=now*unit){
    				comp t1=x[pos],t2=x[pos+tt]*now;
    				x[pos]=t1+t2,x[pos+tt]=t1-t2;
    			}
    		}
    	}
    	if(kd<0.0){for(int i=0;i<len;i++) x[i].r/=(len*1.0);}
    }
    void tms(int *x,int *x1,int *x2,int n1,int n2){
    	if(n1+n2<=120){
    		memset(S,0,sizeof(int)*(n1+n2+1));
    		for(int i=0;i<=n1;i++){
    			for(int j=0;j<=n2;j++) S[i+j]+=mul(x1[i],x2[j]);
    		}
    		for(int i=0;i<=n1+n2;i++) x[i]=S[i]%mod;
    	}
    	else{
    		int len=1,nw=-1;for(;len<=n1+n2+1;len<<=1,nw++);
    		for(int i=1;i<len;i++) rev[i]=(rev[i>>1]>>1)|((i&1)<<nw);
    		for(int i=0;i<=n1;i++) A[i]=comp(x1[i]*1.0,0.0);
    		for(int i=0;i<=n2;i++) B[i]=comp(x2[i]*1.0,0.0);
    		for(int i=n1+1;i<=len;i++) A[i]=comp(0.0,0.0);
    		for(int i=n2+1;i<=len;i++) B[i]=comp(0.0,0.0);
    		FFT(A,len,1.0),FFT(B,len,1.0);
    		for(int i=0;i<len;i++) A[i]=A[i]*B[i]; FFT(A,len,-1.0);
    		for(int i=0;i<=n1+n2;i++) x[i]=llround(A[i].r)%mod;
    	}
    }
    void solve(int *x,int L,int R){
    	if(L==R){x[0]=1,x[1]=G[L];return;}
    	int mid=((L+R)>>1),ls,rs; ls=mid-L+1,rs=R-mid;
    	solve(x,L,mid),solve(x+ls+1,mid+1,R);
    	tms(x,x,x+ls+1,ls,rs);
    }
    int main(){
    	fac[0]=ifac[0]=1,G[1]=9,G[2]=243,G[3]=16224,G[4]=46489;
    	for(int i=1;i<mod;i++) fac[i]=mul(fac[i-1],i),ifac[i]=qpow(fac[i],mod-2);
    	for(int i=5,K,pre,last=3797,rem;i<M;i++){
    		for(K=(i-2)*6,pre=K-6;pre<K;pre++) last=add(add(last,last),lucas(pre,i-2));
    		last=mus(last,lucas(K,i-1)),rem=last;
    		for(int k=0;k<=4;k++) upd(G[i],mul(P[k],rem)),upd(rem,lucas(K,i-k-1));
    	}
    	for(int T=read(),ans=0;T;--T,ans=0){
    		n=read(),m=read(),solve(F,1,n);
    		for(int i=n;i>=m;i--) upd(ans,F[i]);
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
  • 相关阅读:
    【洛谷5304】[GXOI/ZOI2019] 旅行者(二进制分组+最短路)
    【LOJ6485】LJJ 学二项式定理(单位根反演)
    【CF932E】Team Work(第二类斯特林数简单题)
    【CF960G】Bandit Blues(第一类斯特林数)
    【洛谷4689】[Ynoi2016] 这是我自己的发明(莫队)
    【洛谷5355】[Ynoi2017] 由乃的玉米田(莫队+bitset)
    【洛谷5268】[SNOI2017] 一个简单的询问(莫队)
    【洛谷4688】[Ynoi2016] 掉进兔子洞(莫队+bitset)
    【洛谷3653】小清新数学题(数论)
    【洛谷6626】[省选联考 2020 B 卷] 消息传递(点分治基础题)
  • 原文地址:https://www.cnblogs.com/OYJason/p/9751387.html
Copyright © 2011-2022 走看看