• 洛谷 P2183 [国家集训队]礼物


    题目描述

    一年一度的圣诞节快要来到了。每年的圣诞节小E都会收到许多礼物,当然他也会送出许多礼物。不同的人物在小E心目中的重要性不同,在小E心中分量越重的人,收到的礼物会越多。小E从商店中购买了n件礼物,打算送给m个人,其中送给第i个人礼物数量为wi。请你帮忙计算出送礼物的方案数(两个方案被认为是不同的,当且仅当存在某个人在这两种方案中收到的礼物不同)。由于方案数可能会很大,你只需要输出模P后的结果。

    输入输出格式

    输入格式:

    输入的第一行包含一个正整数P,表示模;

    第二行包含两个整整数n和m,分别表示小E从商店购买的礼物数和接受礼物的人数;

    以下m行每行仅包含一个正整数wi,表示小E要送给第i个人的礼物数量。

    输出格式:

    若不存在可行方案,则输出“Impossible”,否则输出一个整数,表示模P后的方案数。

    输入输出样例

    输入样例#1: 
    100
    4 2
    1
    2
    输出样例#1: 
    12
    输入样例#2: 
    100
    2 2
    1
    2
    输出样例#2: 
    Impossible
    

    说明

    【样例说明】

    下面是对样例1的说明。

    以“/”分割,“/”前后分别表示送给第一个人和第二个人的礼物编号。12种方案详情如下:

    1/23 1/24 1/34

    2/13 2/14 2/34

    3/12 3/14 3/24

    4/12 4/13 4/23

    设P=p1^c1 * p2^c2 * p3^c3 * … *pt ^ ct,pi为质数。

    对于15%的数据,n≤15,m≤5,pi^ci≤10^5;

    在剩下的85%数据中,约有60%的数据满足t≤2,ci=1,pi≤10^5,约有30%的数据满足pi≤200。

    对于100%的数据,1≤n≤10^9,1≤m≤5,1≤pi^ci≤10^5,1≤P≤10^9。

    这个很好想啦,就是模数不是质数会很麻烦。

    没错,这就是一道扩展卢卡斯+中国剩余定理的模板题。

    大致的做法就是先把P质因数分解,然后得到在每个质因数^次数下的答案,最后用中国剩余定理合并。。

    那么现在问题的关键变成了如何求n! mod p^k。

    这个可以拆成三部分(准确的说是最多三部分),

    1:n!中是p倍数的可以直接拆出来,递归求(n/p)! mod p^k。

    2.n!中可能有很多个完整的1~p^k-1,这部分可以先预处理一下(p^k-1)! (当然是不算p的倍数的阶乘,因为我们还要记录一下阶乘中p的次数最后一起处理),然后快速幂一下。

    3.n%(p^k)的部分可以直接乘预处理的了。

    可能第一次写会比较费劲,,,,

    (还有我为什么忘了中国剩余定理了23333,记住是∑a[i]*(P/mod[i])*inv(P/mod[i],mod[i]))

    #include<bits/stdc++.h>
    #define ll long long
    #define maxn 100005
    using namespace std;
    int N,P,m,a[15],MOD;
    int d[15],c[15],mo;
    int D[15],jc[maxn];
    int num,ans[15],tot;
    
    inline int add(int x,int y,const int ha){
    	x+=y;
    	if(x>=ha) return x-ha;
    	else return x;
    }
    
    inline int ksm(int x,int y,const int ha){
    	int an=1;
    	for(;y;y>>=1,x=x*(ll)x%ha) if(y&1) an=an*(ll)x%ha;
    	return an;
    }
    
    struct node{
    	int val,tmp;
    	node operator *(const node &U)const{
    		return (node){val*(ll)U.val%D[mo],tmp+U.tmp};
    	}
    	node operator /(const node &U)const{
    		return (node){val*(ll)ksm(U.val,D[mo]/d[mo]*(d[mo]-1)-1,D[mo])%D[mo],tmp-U.tmp};
    	}
    };
    
    inline void dvd(){
    	for(int i=2;i*(ll)i<=P;i++) if(!(P%i)){
    		d[++num]=i,D[num]=1;
    		while(!(P%i)) P/=i,D[num]*=i,c[num]++;
    		if(P==1) break;
    	}
    	if(P!=1) d[++num]=D[num]=P,c[num]=1;
    }
    
    inline node getjc(int x){
    	node now=(node){1,0};
    	if(x>=d[mo]) now=now*getjc(x/d[mo]),now.tmp+=x/d[mo];
    	
    	if(x>=D[mo]) now=now*(node){ksm(jc[D[mo]-1],x/D[mo],D[mo]),0};
    	
    	now=now*(node){jc[x%D[mo]],0};
    	return now;
    }
    
    inline node getC(int x,int y){
    	return getjc(x)/getjc(y)/getjc(x-y);
    }
    
    inline void solve(int x){
        jc[0]=1,mo=x;
    	for(int i=1;i<D[x];i++){
    		jc[i]=jc[i-1];
    		if(i%d[x]) jc[i]=jc[i]*(ll)i%D[x];
    	}
    
    	ans[x]=1;
    	int lef=N;
    	node now;
    	for(int i=1;i<=m;i++){
    		now=getC(lef,a[i]);
    //		printf("%d %d %d %d
    ",lef,a[i],now.val,now.tmp);
    		ans[x]=ans[x]*(ll)now.val%D[x]*(ll)ksm(d[x],now.tmp,D[x])%D[x];
    		lef-=a[i];
    	}	
    }
    
    inline int CRT(){
    	int an=0;
    	for(int i=1;i<=num;i++){
    		an=add(an,ksm(MOD/D[i],D[i]/d[i]*(d[i]-1)-1,D[i])*(ll)(MOD/D[i])%MOD*(ll)ans[i]%MOD,MOD);
    	}
    	return an;
    }
    
    int main(){
    	scanf("%d%d%d",&P,&N,&m),MOD=P;
    	for(int i=1;i<=m;i++) scanf("%d",a+i),tot+=a[i];
    	
    	if(tot>N){
    		puts("Impossible");
    		return 0;
    	}
    	
    	dvd();
    
    	for(int i=1;i<=num;i++) solve(i);
    
    	printf("%d
    ",CRT());
    	return 0;
    }
    

      

  • 相关阅读:
    Java基础其他
    java网络编程
    java多线程编程
    正则表达式--位置匹配和组
    设计模式
    深入 Java Web
    mysql 好用的sql语句
    spring boot 发送邮件
    dubbo的spi机制
    原理分析dubbo分布式应用中使用zipkin做链路追踪
  • 原文地址:https://www.cnblogs.com/JYYHH/p/8458500.html
Copyright © 2020-2023  润新知