zoukankan      html  css  js  c++  java
  • 「SOL」礼物(BZOJ)

    写了个 TODO List,现在不会忘写博客了


    # 题面

    > Link DarkBZOJ 2142

    (n) 件不同的礼物送给 (m) 个人,其中送给第 (i) 个人礼物数量为 (w_i)。计算出送礼物的方案数(两个方案被认为是不同的,当且仅当存在某个人在这两种方案中收到的礼物不同)。由于方案数可能会很大,输出模 (P) 后的结果。

    (P=p_1^{c_1}p_2^{c_2}cdots p_t^{c_t})(p_i) 为质数,则保证 (p_i^{c_i}le10^5)


    # 解析

    普通的 Lucas 定理只能用来求组合数模较小的素数的问题。

    (部分) Lucas 定理

    $$ inom{n}{m}mod p=inom{lfloor frac{n}{p} floor}{lfloor frac{m}{p} floor}inom{nmod p}{mmod p}mod p $$

    (但是似乎不是完整的 Lucas 定理?)

    而 exLucas 定理可以解决 (p) 不是一个质数的问题(只要满足「BZOJ 礼物」这道题对 (p) 的限制即可)。

    首先对 (p) 进行质因数分解 (p=prodlimits_{i=1}^tp_i^{c_i}),设 (x=inom{n}{m}mod p)(x_i=inom{n}{m}mod p_i^{c_i}),则

    [egin{cases} xequiv x_1pmod{p_1^{c_1}}\ xequiv x_2pmod{p_2^{c_2}}\ cdots\ xequiv x_tpmod{p_t^{c_t}} end{cases} ]

    模数互质,可以直接CRT。所以问题转化为「组合数模一个质数的幂 (p^k)」。

    把组合数的式子展开 (inom{n}{m}=frac{n!}{m!(n-m)!}),我们不能直接求 (m!)((n-m)!) 的逆元,因为它们不一定和 (p) 互质。于是想到,可以先把阶乘中 (p) 的因子部分提出来,剩下的与 (p) 互质的部分就可以求逆元。即

    [m!=p^acdot b ]

    其中 (p otmid b)((n-m)!) 同理。于是剩下的问题就是怎么将阶乘分解成上述形式。

    考虑计算 (a):统计 (1,2,cdots,m) 中能提取出多少 (p) 的因子。比较简单,只需要对每个 (p) 的幂 (p^i) 统计 (1sim m) 中有多少 (p^i) 的倍数。可以 (O(log_p m)) 完成。

    再考虑计算 (b),可以递归计算:

    • 先计算出 (1sim m) 中所有 (mathbf{p}) 的倍数的数之积模 (p^k)
    • 再将 (p) 的倍数单独提出来,除以 (p) 后即为 (1simlfloor frac{m}{p} floor),递归处理。

    可以先预处理出 (f_i) 表示 (1sim i) 中所有不为 (p) 的倍数的数的积模 (p^k),则第一步的答案为

    [f_{p^k-1}^{lfloorfrac{m}{p} floor}cdot f_{mmod p^k} ]

    现在就可以快速计算组合数了。

    对于这道题,可以直接拆开组合数,计算

    [frac{n!}{w_1!w_2!cdots w_m!(n-w_1-w_2-cdots-w_m)!} ]

    然后直接用 exLucas 的拆解阶乘的方法,再用CRT合并(就不需要真的去计算每个组合数)。


    # 源代码

    点击展开/折叠代码
    /*Lucky_Glass*/
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    
    typedef long long llong;
    const int N=100,B=1e5+10;
    #define con(type) const type &
    int mul(con(int)a,con(int)b,con(int)mod){return int(1ll*a*b%mod);}
    int ina_pow(con(int)a,con(llong)b,con(int)mod){return b?mul(ina_pow(mul(a,a,mod),b>>1,mod),(b&1)?a:1,mod):1;}
    typedef pair<int,llong> pii;
    
    int p[N],pc[N],fac[B],phi[N];
    llong rem[N],w[N],mod;
    int n,m,np;
    
    void init(){
    	llong now=mod;
    	for(int i=2;1ll*i*i<=now;i++)
    		if(now%i==0){
    			p[++np]=i,pc[np]=1;
    			while(now%i==0) now/=i,pc[np]*=i;
    		}
    	if(now>1) np++,p[np]=pc[np]=(int)now;
    	for(int i=1;i<=np;i++) rem[i]=mod/pc[i],phi[i]=pc[i]/p[i]*(p[i]-1);
    }
    pii calc(con(llong)x,con(int)p0,con(int)pc0){
    	if(x<p0) return make_pair(fac[x],0);
    	pii res=calc(x/p0,p0,pc0);
    	return make_pair(mul(mul(res.first,fac[x%pc0],pc0),ina_pow(fac[pc0-1],x/pc0,pc0),pc0),res.second+x/p0);
    }
    int func(int p0,int pc0,int phi0){
    	fac[0]=1;
    	for(int i=1;i<pc0;i++) fac[i]=mul(fac[i-1],i%p0?i:1,pc0);
    	pii up=calc(n,p0,pc0);
    	fac[pc0-1]=ina_pow(fac[pc0-1],phi0-1,pc0);
    	for(int i=pc0-2;i;i--)
    		fac[i]=mul(fac[i+1],(i+1)%p0?i+1:1,pc0);
    	pii blw(1,0);
    	for(int i=1;i<=m;i++){
    		pii res=calc(w[i],p0,pc0);
    		blw.second+=res.second;
    		blw.first=mul(blw.first,res.first,pc0);
    	}
    	return mul(mul(up.first,blw.first,pc0),ina_pow(p0,up.second-blw.second,pc0),pc0);
    }
    int main(){
    	scanf("%lld%d%d",&mod,&n,&m);
    	init();
    	llong total=0;
    	for(int i=1;i<=m;i++){
    		scanf("%lld",&w[i]);
    		total+=w[i];
    	}
    	if(total>n){printf("Impossible
    ");return 0;}
    	if(total<n) w[++m]+=n-total;
    	llong sum=0;
    	for(int i=1;i<=np;i++){
    		llong now=mul(func(p[i],pc[i],phi[i]),ina_pow(rem[i],phi[i]-1,pc[i]),mod);
    		now=mul(now,rem[i],mod);
    		sum=(sum+now)%mod;
    	}
    	printf("%lld
    ",(sum%mod+mod)%mod);
    	return 0;
    }
    

    THE END

    Thanks for reading!

    史书缺失的那页
    遗忘了主角
    有人传说这一切
    如流光幻夜

    ——《流光幻夜》By 司夏

    > Link 流光幻夜-网易云

    欢迎转载٩(๑❛ᴗ❛๑)۶,请在转载文章末尾附上原博文网址~
  • 相关阅读:
    linux下的磁盘挂载
    shell中的循环语句while
    hadoop安装和配置
    shell 命令 创建/删除 软连接 ln -s
    azkaban disable 停用部分工作流
    git dev 分支merge到master
    shell 命令 zip unzip
    git代码同步服务器代码需要注意的问题
    shell 命令 if elif else fi 用法
    python 引入本地 module
  • 原文地址:https://www.cnblogs.com/LuckyGlass-blog/p/14430477.html
Copyright © 2011-2022 走看看