hdu 5446

卢卡斯定理:

求c(n,m)%p(p是一个素数),设n=a[x]*p^x+a[x-1]*p^(x-1)+……+a[0]*p^0,m=b[x]*p^x+b[x-1]*p^(x-1)+……+b[0]*p^0,(如果m化成p进制,不够x+1位,就补0),则c(n,m)=c(a0,b0)*c(a1,b1)*……*c(ax,bx)  (%p).

证:

首先(1+x)^(p^z)=1+x^(p^z) (%p),其次f(x)=g(x) (%p),说明f(x)中x^y的系数模p等于g(x)中x^y的系数模p

那么 (1+x)^n=(1+x)^(a[x]*p^x+a[x-1]*p^(x-1)+……+a[0]*p^0)                                 (%p)

                       =(1+x)^(a[x]*p^x)*(1+x)^(a[x-1]*p^(x-1))*……*(1+x)^(a[0]*p^0)      (%p)

                       =(1+x^(p^x))^a[x]*(1+x^(p^(x-1)))^a[x-1]*……*(1+x^(p^0))^a[0]     (%p)


左边x^m的系数是c(n,m),因为一个数的p进制只有一种形式,所以右边x^m的系数是c(ax,bx)*c(a(x-1),b(x-1))*……*c(a0,b0),他们%p的余数相等。


中国剩余定理:

互质版:

主要是构造一个通解:

x%p1=a1

x%p2=a2

……

x%py=ay


m=p1*p2*……py

mi=m/pi; mj

设mii是mi关于pi的逆元,所以构造

x=a1*m1*m11+a2*m2*m22+a3*m3*m33+……+ay*my*myy

非互质版:

 参考链接:http://yzmduncan.iteye.com/blog/1323599


注:(n1/d)^(-1)代表逆元

先逆着验证:

x=k'*n1*n2/d+n1*(a2-a1)/d*(n1/d)^(-1)+a1;

当x%n1时,显然是a1,

当x%n2时, 因为(n1/d)^(-1)是关于(n2/d)的,所以(n1/d)*n1=k*n2+d,带入得 k*n1*n2/d+(a2-a1)/d*(k*n2+d)+a1,它模上n2时,得到a2.


注:(a*b)%c=((a%c)*(b%c))%c,成立,当且仅当a,b均为整数。

链接里是正着推的,大概思路就是把两个式子

x=k1*n1+a1

x=k2*n2+a2

合成一个式子,然后解这个式子,求出k1,再代入第一个式子。

其实求得过程中也可以一直%n2,也就是说 k1=(a2-a1)*(n1^-1)  %n2,最后再代入,但是因为n1关于n2的逆元不一定存在,所以化成求n1/d关于n2/d的逆元,这样就保证逆元一定是存在的。


注:最后转化成x%(n1*n2/d)的值,其实可以一直是x%(n2/d)到最后,为什么要转化呢?因为x所有的可能解模(n1*n2/d)相等,假设等于y,而y+k*(n1*n2/d)都是x的可能解,所以最后要转化成x%(n1*n2/d),虽然x的可能解模上(n2/d)相等,假设等于z,但是z+k(n2/d) 不都是x的可能解。

所以整体大概就是这样一个思路,反反复复看了好几次,每一次都是无疾而终,感觉看倒是顺的下来,就是不知道它在做些什么,为什么要这样做。敲打,这次感觉窥探到了一丝奥秘。


假设p=p1*p2*……*pk

对于为什么可以用中国剩余定理呢?假设通过中国剩余定理求出来某个具体答案,那么这个答案加上k倍的p(k属于整数)就是这个x(含义见中国剩余定理中x的含义)的所有可取值,最后我们要求的这个组合数是模上p的,所以在[0,p)之间x只有一个取值,所以就是那个,没有疑问。


代码参考:http://blog.sina.com.cn/s/blog_12fea76590102w6ts.html

用java写的超时代码,但是估计不只是超时,还有wr,但是不能扔呀,里面全是错误经验。本来觉得有大数,就用java写吧,结果谁知道,完全有另一种更简单的方法,(已在代码中标出)忽然想起老师说的那句话,一个代码已经发现很多错,并不代表剩下的错很少,相反,剩下的错很多很多。

(最近迷上了学校里的胡辣汤泡油条,非常好吃生气

(java,不ac)

import java.math.BigInteger;
import java.util.Scanner;

public class Main{
	static Scanner in=new Scanner(System.in);
	static int N=20;
	static long[] a=new long[N];
	static long[] b=new long[N];
	static long[] ar=new long[2];
	
	/*static long inv(long x,long y){
		BigInteger ans=new BigInteger("1");
		BigInteger yy=new BigInteger(String.valueOf(y));
		long tempy=y-2;
		BigInteger tempx=new BigInteger(String.valueOf(x));
		
		while(tempy!=0){
			if(tempy%2!=0){
				ans=ans.multiply(tempx).mod(yy);
			}
			tempy=tempy/2;
			tempx=tempx.multiply(tempx).mod(yy);
		}
		
		return ans.longValue();
	}*/
	
	static int inv(int x,long y){
		long ans=1;
		long tempy=y-2;
		long tempx=x;
		
		while(tempy!=0){
			if(tempy%2!=0){
				ans=ans*tempx%y;
			}
			tempy=tempy/2;
			tempx=tempx*tempx%y;
		}
		
		return (int)ans;
	}
	
	static long lukasi(long n,long m,long p){
		if(n<p&&m<p){
			if(m>n){
				return (long)0;//加了一个强制转化
			}
			else if(m==n){
				return (long)1;//加了一个强制转化
			}
			else if(m==0){
				return (long)1;
			}
			else{
				long ans=1;
				
				for(int i=1;i<=m;i++){//应该是<=m,而不是<m
					ans=ans*inv(i,p)%p*(n-i+1)%p;
				}
				
				return ans;
			}
		}
		else{
			return lukasi(n/p,m/p,p)*lukasi(n%p,m%p,p)%p;
		}
	}
	
	/*static void merge(int x,int y){
		long c=b[y]-b[x];
		long d=1;
		long n1=a[x],n2=a[y],a1=b[x],a2=b[y];
		
		c=(c%n2+n2)%n2;//这里模数写成了2
		c=c/d;
		n1=n1/d;
		n2=n2/d;
		c=c*inv(n1,n2);
		c=c%n2;
		BigInteger tempc= new BigInteger(String.valueOf(c));
		a[y]=(new BigInteger(String.valueOf(n1))).multiply(new BigInteger(String.valueOf(n2))).longValue();
		tempc=tempc.multiply(new BigInteger(String.valueOf(n1))).add(new BigInteger(String.valueOf(a1))).mod(new BigInteger(String.valueOf(a[y])));
		b[y]=tempc.longValue();
		
		return;
	}*/
	
	static void exgcd(/*BigInteger[] arlong[] ar,*/long x,long y){
		if(y==0){
			//ar[0]=new BigInteger("1");
			//ar[1]=new BigInteger("1");
			ar[0]=1;//为什么这里不需要大数?
			ar[1]=1;
		}
		else{
			//BigInteger[] br =new BigInteger[2];
			long[] br=new long[2];
			
			exgcd(/*br,*/y,x%y);
			br[0]=ar[1];
			//ar[1]=br[0].subtract((new BigInteger(String.valueOf(x/y)).multiply(br[1])));
			br[1]=ar[0]-x/y*ar[1];
			ar[0]=br[0];
			ar[1]=br[1];
		}
		
		return;
	}
	
	static long mult(long x,long y,long p){
		long ans=0;
		
		while(y!=0){
			if((y%2)!=0){
				ans=(ans+x)%p;
			}
			y=y/2;
			x=(x+x)%p;
		}
		
		return ans;
	}
	
	static long query(long n,long m,int k){
		
		for(int i=0;i<k;i++){
			b[i]=lukasi(n,m,a[i]);
		}
		
		/*for(int i=0;i<k-1;i++){
			//System.out.println(a[i]+" "+b[i]+" "+a[i+1]+" "+b[i+1]);
			merge(i,i+1);
		}
		
		//System.out.println(b[k-1]);
		return b[k-1];*/
		
		long q=1;
		for(int i=0;i<k;i++){
			q*=a[i];
		}
		
		long ans=0;//因为加法不需要大数,所以把ans从BigInteger换回long
		BigInteger modnum=new BigInteger(String.valueOf(q));
		for(int i=0;i<k;i++){
			//BigInteger[] ar=new BigInteger[2];//这里要new,不要null
			//long[] ar=new long[2];
			//BigInteger temp=new BigInteger(String.valueOf(q/a[i]));
			
			exgcd(/*ar,*/q/a[i],a[i]);//这里没有返回
			
			//temp=temp.multiply(new BigInteger(String.valueOf(b[i])));
			//temp=temp.multiply(new BigInteger(String.valueOf(ar[0]))).mod(modnum);
			//ans=(ans+temp.longValue())%q;//把q弄成BigInteger,这样就不用每次都转化了,果真有tle变成了wr
			//不用大数,会tle
			
			ar[0]=(ar[0]%q+q)%q;//ar[0]在这里应该做这样的处理,否则不能保证ar[0]的范围
			//System.out.println(ar[0]+" "+q+" "+a[i]+" "+b[i]);
			ans=(mult(mult(q/a[i],b[i],q),ar[0],q)+ans)%q;//加法不会溢出,因为10^18+10^18<2^64
			//System.out.println(ans);
		}
		
		return ans;
	}
	
	static public void main(String args[]){
		int t;
		long n,m;
		int k;
		
		t=in.nextInt();
		while((t--)!=0){
			n=in.nextLong();
			m=in.nextLong();
			k=in.nextInt();
			
			for(int i=0;i<k;i++){
				a[i]=in.nextLong();
			}
			
			System.out.println(query(n,m,k));
		}
	}
}

(c++,已ac)

#include<stdio.h>
#include<string.h>
#include<iostream>
using namespace std;
#define N 20

long long int a[N],b[N];

void exgcd(long long int *x,long long int *y,long long int anum,long long int bnum){
    if(bnum==0){
        *x=1;
        *y=0;
    }
    else{
        long long int tempx,tempy;
        exgcd(&tempx,&tempy,bnum,anum%bnum);
        *x=tempy;
        *y=tempx-anum/bnum*tempy;
    }

    return;
}

long long int inv(int x,long long int y){
    long long int ans=1,tempx=x,tempy=y-2;

    while(tempy){
        if(tempy%2){//这里把tempy写成了tempx,所以wr了
            ans=ans*tempx%y;
        }
        tempy=tempy/2;
        tempx=tempx*tempx%y;
    }

    return ans;
}

long long int lukasi(long long int n,long long int m,long long int q){
    if(n<q&&m<q){
        if(m==0||m==n){
            return 1;
        }
        else if(m>n){
            return 0;
        }
        else if(m==1){
            return n;
        }
        else{
            long long int ans=1;
            for(int i=1;i<=m;i++){
                ans=ans*inv(i,q)%q*(n-i+1)%q;
            }

            return ans;//忘了写return,请带脑袋好吗?
        }
    }
    else{
        return lukasi(n/q,m/q,q)*lukasi(n%q,m%q,q)%q;
    }
}

long long int mult(long long int x,long long int y,long long int q){//这道题get到的点在这里
    long long int ans=0;

    while(y){
        if(y%2){
            ans=(ans+x)%q;//这里把ans写成了y,写代码时请带脑袋!
        }
        y=y/2;
        x=(x+x)%q;
    }

    return ans;
}

long long int query(long long int n,long long int m,int k){
    for(int i=0;i<k;i++){
        b[i]=lukasi(n,m,a[i]);
    }

    long long int q=1;
    for(int i=0;i<k;i++){
        q=q*a[i];
    }

    long long int ans=0;//加法初始化是0,乘法初始化才是1
    for(int i=0;i<k;i++){
        long long int x,y;
        exgcd(&x,&y,q/a[i],a[i]);
        //printf("%lld %lld %lld %lld\n",q,a[i],b[i],x);
        x=(x%q+q)%q;
        ans=(ans+mult(mult(q/a[i],b[i],q),x,q))%q;//printf("wo shi da hao ren\n");//调试:这里没有回来
        //printf("%lld\n",ans);
    }

    return ans;
}

int main(){
    int t;
    long long int n,m;
    int k;

    scanf("%d",&t);
    while(t--){
        scanf("%lld%lld%d",&n,&m,&k);
        for(int i=0;i<k;i++){
            scanf("%lld",&a[i]);
        }

        printf("%lld\n",query(n,m,k));
    }
}



posted @ 2015-09-16 21:05  buzhidaohahaha  阅读(137)  评论(0编辑  收藏  举报