算法竞赛进阶指南 0x30 数学知识

算法竞赛进阶指南 0x30 数学知识

本书有在线测评网站的支持,是一大亮点,又是NOI经历者编写的,故赶紧入手一本,抓紧时间研读,希望能通过该书建立较为完整的知识体系。先从最为薄弱,但又想尽快提升的数学入手。2018-2-12 20:33

http://begin.lydsy.com/JudgeOnline/problemset.php?page=38

0x31 质数

//4789: Poj2689 Prime Distance
//有个疑问,2-sqrt(R) 数组得开到1.5*2^15 这已超出内存限制了,写到这里,突然发现是自己的问题,想成1.5*10^15
//实际应该是,1.5*2^15 数组不会超出内存限制
//即数组开到sqrt(2^31)=46500即可,归根结底,还是想得多,动手得少
//处理1<<31遇到障碍,
//赋值1<<31 一直是负数,参考http://blog.csdn.net/hiyohu/article/details/52292634此文 由于编译器将1默认为int类型,将1左移31位时超过了int最长位数,编译器给出一个默认值。想要得到理想中的值需将1强制转换成unsigned long long类型。马上进行修改 .
//处理 v2[2147483648],prime2[2147483648]; 发现 溢出了。
//求助网络,此文代码写得比较合心意https://www.cnblogs.com/kuangbin/archive/2013/05/20/3089840.html
//该题要小心,在计算的过程中,int容易溢出,有些地方要注意强转long long
//原题可参考 https://vjudge.net/problem/POJ-2689
//样例通过,提交Wrong Answer if(L<2)L=2;//漏了此句,一直AC ,确实如此,若无此句,按下面算法,1也是质数,当然是错误的
//若无测试数据,思考无果,只能对照正确代码进行排错,提交AC 2018-2-15 9:33
//该题比较核心的思想:任何一个合数n必定包含一个不超过sqrt(n)的质因数。
//额外的知识,每logN个数中大约有1个质数
//该题断续编了有5天之间,思考量,思维量比较大,建议读者自己先编,收获比直接看解答大得多得多。
#include <stdio.h>
#include <string.h>
int v1[46500],prime1[46500/2],v2[1000100],prime2[1000100/2];//N个数里的质数个数接近logN
void getPrime1(int n){//筛法 //获得2--n==sqrt(R)之间的质数
    int i,j;
    memset(v1,0,sizeof(v1)),prime1[0]=0;//prime1[0]用来统计prime1[]中质数个数
    for(i=2;i<=n;i++){
        if(v1[i]==0)v1[i]=i,prime1[++prime1[0]]=i;//此处写成v1[i]=2//i是质数
        for(j=1;j<=prime1[0];j++){
            if(prime1[j]>v1[i]||prime1[j]*i>n)break;//prime1[j]>v[i] i已经用采用最小质数v[i]处理过了,避免重复处理,跳出循环;prime1[j]*i>n 数据已经超出排查范围,跳出循环
            v1[i*prime1[j]]=prime1[j];
        }
    }
}
void getPrime2(int L,int R){//获得L--R之间的质数
    int i,j,start;
    memset(v2,0,sizeof(v2)),prime2[0]=0;
    if(L<2)L=2;//漏了此句,一直AC ,确实如此,若无此句,按下面算法,1也是质数,当然是错误的
    for(i=1;i<=prime1[0]&&(long long)prime1[i]*prime1[i]<=R;i++){//请注意(long long)prime1[i]*prime1[i]<=R技巧,prime1[i]*prime1[i]容易超出int范围//i<=prime1[0] 不能超出质数范围, prime1[i]*prime1[i]<=R 不能超出右边界
        start=L/prime1[i]+(L%prime1[i]>0);//要求start*prime1[i]i>=L;
        if(start==1)start=2;//start必须从2开始 可以参考getPrime1中代码  for(i=2;i<=n;i++) i从2开始
        for(j=start;(long long)j*prime1[i]<=R;j++)v2[j*prime1[i]-L]=1;//请注意(long long)j*prime1[i]<=R j*prime1[i]容易超出int范围//此处存在重复,在思考如何处理得更好//请注意 j*prime1[i]-L处理手法,偏移量处理方式,此处 本人 一直未想到,只好参考他人代码,不过还是有收获的  
    }
    for(i=0;i<=R-L;i++)
        if(v2[i]==0)prime2[++prime2[0]]=i+L;//此处写成 prime2[++prime2[0]]=i 在进行样例测试中排查出来,注意了,别忘了将偏移量加回去
}
int main(){
    int L,R,x1,x2,y1,y2,i;
    getPrime1(46500);//46500>sqrt(2^31);
    while(scanf("%d%d",&L,&R)!=EOF){
        getPrime2(L,R);
        if(prime2[0]<2){printf("There are no adjacent primes.\n");continue;}//此处写成break;经样例测试排查出来//区间内质数个数小于两个质数
        x1=0,x2=1000100,y1=0,y2=0;
        for(i=1;i<prime2[0];i++){//请注意prime2[0]最大值接近log1000000=10*10=100
            if(x2-x1>prime2[i+1]-prime2[i])x1=prime2[i],x2=prime2[i+1];
            if(y2-y1<prime2[i+1]-prime2[i])y1=prime2[i],y2=prime2[i+1];
        }
        printf("%d,%d are closest, %d,%d are most distant.\n",x1,x2,y1,y2);//建议字符串直接拷贝输出
    }
    return 0;
}
 

若读者觉得意犹未尽,想练练筛法,可以试试下面这道

//P3383 【模板】线性筛素数
//https://www.luogu.org/problemnew/show/P3383 在线测评网站
//2018-2-15 9:57 AC
#include <stdio.h>
#include <string.h>
int v[10000100],prime[10000100/2];
void getPrime(int n){//筛法模板 线性筛法 源自《算法竞赛进阶指南》 时间效率比之前Eratoshenes算法要提高一倍
    int i,j;
    memset(v,0,sizeof(v)),prime[0]=0;
    for(i=2;i<=n;i++){
        if(v[i]==0)v[i]=i,prime[++prime[0]]=i;
        for(j=1;j<=prime[0];j++){
            if(prime[j]>v[i]||prime[j]*i>n)break;
            v[i*prime[j]]=prime[j];
        }
    }
}
int main(){
    int n,m,a;
    scanf("%d%d",&n,&m);
    getPrime(n);
    while(m--){
        scanf("%d",&a);
        if(v[a]==a)printf("Yes\n");
        else printf("No\n");
    }
    return 0;
}

 

0x32 约数

//4790: Lydsy1053 反素数
//P1463 [HAOI2007]反素数 洛谷https://www.luogu.org/problemnew/show/P1463
//先看题,没看书之前,发现,要筛出1--N中 约数的个数,1<=N<=2,000,000,000 就算是O(n)的算法也要超时
//好吧,先学习书。
//看书学习该题的一系列证明过程,发现真是值回书费,感觉确实内容前后呼应,学习完该章,应能建立一定的数学体系
//https://www.cnblogs.com/zhber/p/4036041.html此文代码写得不错,
//跟踪了上述算法,发现与本人思路正好相反,期待水平提升后,能独立将本人思路编写成功。
//按上述算法编写下来,还是感觉到递归写得不太好
//样例通过,提交AC 2018-2-15 19:36 递归里未必有回溯,回溯里必有递归
#include <stdio.h>
int n,ans=2100000000,mx=0,c[12],prime[]={0,2,3,5,7,11,13,17,19,23,29};//c[i]统计对应prime[i]的个数
void dfs(int pos,int cnt,long long mult){//pos当前质数在prime中的位置,cnt当前约数个数,mult当前对应的约数的乘积
    if(mx<cnt||mx==cnt&&ans>mult)mx=cnt,ans=mult;
    c[pos]=0;//重置当前位置质数个数
    while(mult*prime[pos]<=n&&c[pos]<=c[pos-1]){//请注意c[]是全局变量
        c[pos]++,mult*=prime[pos];
        dfs(pos+1,cnt*(c[pos]+1),mult);
    }
}
int main(){
    scanf("%d",&n);
    c[0]=32;//质因数幂次和不超过31
    dfs(1,1,1);
    printf("%d",ans);
    return 0;
}

//4791: Lydsy1257 余数之和
//1<=n ,k<=10^9 数据范围,告诉我,要学新知识了

//看书,面强看懂大部分,但还有些细节不清楚,模拟了一堆数据[k/i]*i k从1-11都模拟了一遍,有了写感觉

[1/1]*1=1

[2/1]*1=2
[2/2]*2=2

[3/1]*1=3
[3/2]*2=2
[3/3]*3=3

[4/1]*1=4
[4/2]*2=4
[4/3]*3=3
[4/4]*4=4

[5/1]*1=5
[5/2]*2=4
[5/3]*3=3
[5/4]*4=4
[5/5]*5=5

[6/1]*1=6
[6/2]*2=6
[6/3]*3=6
[6/4]*4=4
[6/5]*5=5
[6/6]*6=6

[7/1]*1=7
[7/2]*2=6
[7/3]*3=6
[7/4]*4=4
[7/5]*5=5
[7/6]*6=6
[7/7]*7=7

[8/1]*1=8
[8/2]*2=8
[8/3]*3=6
[8/4]*4=8
[8/5]*5=5
[8/6]*6=6
[8/7]*7=7
[8/8]*8=8

[9/1]*1=9
[9/2]*2=8
[9/3]*3=9
[9/4]*4=8
[9/5]*5=5
[9/6]*6=6
[9/7]*7=7
[9/8]*8=8
[9/9]*9=9

[10/1]*1=10
[10/2]*2=10
[10/3]*3=9
[10/4]*4=8
[10/5]*5=10
[10/6]*6=6
[10/7]*7=7
[10/8]*8=8
[10/9]*9=9
[10/10]*10=10

[11/1]*1=11
[11/2]*2=10
[11/3]*3=9
[11/4]*4=8
[11/5]*5=10
[11/6]*6=6
[11/7]*7=7
[11/8]*8=8
[11/9]*9=9
[11/10]*10=10
[11/11]*11=11

//参考http://blog.csdn.net/u013598409/article/details/47037031

//参考http://blog.csdn.net/loi_dqs/article/details/50522975根据此文,动手证明了一遍

//摘抄部分:


//添加的证明部分:

 

//a1=w*L an=w*R 求和S=(a1+an)*(n-1+1)/2
//即S=(w*L+w*R)*(R-L+1)/2 整理可得S=w*(L+R)*(R-L+1)/2
//样例通过,提交AC 2018-2-16 21:37
#include <stdio.h>
typedef long long LL;
int main(){
    LL L,R,w,n,k,i,ans;
    scanf("%lld%lld",&n,&k);
    ans=n*k;
    if(n>k)n=k;//k/i i>k k/i=0故 i>k部分对结果无影响,故将n=k
    for(i=1;i<=n;i=R+1){
        L=i,w=k/i,R=k/w;
        if(R>n)R=n;//越界判断
        ans-=w*(L+R)*(R-L+1)/2;
    }
    printf("%lld",ans);
    return 0;
}

//4792: Noip2009 Hankson的趣味题
//http://codevs.cn/problem/1172/  1172 Hankson 的趣味题
//https://www.luogu.org/problemnew/show/P1072洛谷 P1072 Hankson 的趣味题
//http://www.cnblogs.com/cellular-automaton/p/7475678.html此文思路不错,摘抄如下:
//x与a0的最大公约数为a1,那么我们把x/=a1,a0/=a1之后,x和a0不会再有除了1之外的公约数。
//承接上文思路,此文证明不错http://blog.csdn.net/nuclearsubmarines/article/details/77603154
//https://www.luogu.org/problemnew/solution/P1072?&page=4代码写得不错
//样例通过,提交AC 2018-2-17 17:18
#include <stdio.h>
int gcd(int a,int b){
    return b?gcd(b,a%b):a;//此处写成 return b?a:gcd(b,a%b); 大失水准,没想到是这里写错,查了好长时间
}
int main(){
    int n,a0,a1,b0,b1,i,x,ans;
    scanf("%d",&n);
    while(n--){
        ans=0;
        scanf("%d%d%d%d",&a0,&a1,&b0,&b1);
        for(i=1;i*i<=b1;i++)
            if(b1%i==0){
                x=i;
                if(x%a1==0&&gcd(x/a1,a0/a1)==1&&gcd(b1/x,b1/b0)==1)ans++;//不写成if(x%a1==0&&gcd(x,a0)==a1&&gcd(x,b0)*b1==x*b0)ans++;原因 x*b0容易超出int范围
                x=b1/i;//b1的另一个约数
                if(x!=i&&x%a1==0&&gcd(x/a1,a0/a1)==1&&gcd(b1/x,b1/b0)==1)ans++;//不写成if(x!=i&&x%a1==0&&gcd(x,a0)==a1&&gcd(x,b0)*b1==x*b0)ans++;原因 x*b0容易超出int范围
            }
        printf("%d\n",ans);
    }
    return 0;
}
//4793: Poj3090 Visible Lattice Points
//https://vjudge.net/problem/POJ-3090可查看该题题目
//首先,为什么是2倍关系,https://yq.aliyun.com/articles/6472此文说得不错,摘抄如下:
//首先ans[1]=3,不经过其他点也就是(x,y)点的gcd(x,y)=1,也就是x,y,互素。所以就是每一行上的个数为phi(n)*2因为行和列都算上就是两倍关系。
//要更深入的了解该题,模拟是少不了的
//size=1 visible points (1,0)(1,1)(0,1)
//size=2 visible points (1,0)(2,1)(1,1)(1,2)(0,1)
//size=3 visible points (1,0)(3,1)(2,1)(3,2)(1,1)(2,3)(1,2)(1,3)(0,1)
//样例通过,提交AC 2018-2-17 22:56
#include <stdio.h>
#include <string.h>
#define maxn 1100
int v[maxn],prime[maxn],phi[maxn];
void Euler(int n){//线性筛法,通质数
    int i,j;
    memset(v,0,sizeof(v)),prime[0]=0;//prime[0]用来计数质数个数
    for(i=2;i<=n;i++){
        if(v[i]==0)v[i]=i,prime[++prime[0]]=i,phi[i]=i-1;//i是质数
        for(j=1;j<=prime[0];j++){
            if(prime[j]>v[i]||prime[j]*i>n)break;//之前已经筛过
            v[i*prime[j]]=prime[j];
            phi[i*prime[j]]=phi[i]*(i%prime[j]?prime[j]-1:prime[j]);//此处写成 phi[i*prime[j]]=phi[prime[j]]*(i%prime[j]?i-1:i);
        }
    }
}
int main(){
    int c,n,k=0,i,sum;
    scanf("%d",&c);
    while(c--){
        scanf("%d",&n);
        Euler(n);
        k++,sum=0;
        for(i=2;i<=n;i++)sum+=phi[i];
        printf("%d %d %d\n",k,n,3+2*sum);
    }
    return 0;
}

 

0x33 同余

//4794: The Luckiest Number
//https://vjudge.net/problem/POJ-3696可看原题
//http://blog.csdn.net/qiqijianglu/article/details/7926478此文思路写得不错,摘抄如下:
//思路:这个数一定可以写成11……11*8=(10^0+10^1……+10^(k-1))*8
//化简得8*(10^k-1)/9=mL (等比数列,笔者注)
//-->8*(10^k-1)=9mL
//-->d=gcd(8,L)   8*(10^k-1)/d=9mL/d
//-->p=8/d   q=9mL/d     p*(10^k-1)=q
//--->因为p,q互质,10^k=1 mod q
//由欧拉定理可知,当q与10互质的时候,10^(phi(q))=1 mod q
//所以无解的时候就是q与10不互质的时候,此外我们求phi(q)的所有因子,按小到大排序,第一个满足10^x =1 mod q的输出。
//上述思路在于,得出 8*(10^k-1)/9=mL 写得不错
//http://blog.csdn.net/wzq_QwQ/article/details/46699751此文让人有阅读学习的兴趣

//http://blog.csdn.net/huanghongxun/article/details/50966715此文代码写得够短,让人有编写的兴趣

上图公式解释如下:

因8/gcd(8,n)与n/gcd(8,n)互质,因8与9互质,故8/gcd(8,n)与9互质,即gcd(8/gcd(8,n),9)=1,

故8/gcd(8,n)与9*n/gcd(8,n)互质 即gcd(8/gcd(8,n),9n/gcd(8,n))=1

下图公式解释如下:

因8/gcd(8,n)与9*n/gcd(8,n)互质  因(10^x-1)*8/gcd(8,n)=k*9n/gcd(8,n) 故(10^x-1)一定能整除9n/gcd(8,n)

即9n/gcd(8,n)|(10^x-1)

令 q=9n/gcd(8,n)

10^x≡1(mod q)

上述同余关系,来自同余定理 详见 https://baike.baidu.com/item/%E5%90%8C%E4%BD%99%E5%AE%9A%E7%90%86/1212360?fr=aladdin

摘抄如下:

数论中的重要概念。给定一个正整数m,如果两个整数a和b满足a-b能够被m整除,即(a-b)/m得到一个整数,那么就称整数a与b对m同余,记作a≡b(mod m)。对模m同余是整数的一个等价关系。

用了三天时间,上述证明总算是弄明白了,可以说,没有本书的支持,学到这里很是困难。2018-2-19 19:46

//http://blog.csdn.net/yhrun/article/details/6908470再看此文,发现能看懂了

//在编写快速幂时,提交,一直Wrong Answer ,搜索网络,发现 由于在进行快速幂求模的时候数据会超出long long 的表示范围,因此要进行优化。
//惊呆了,快速幂也会 溢出,马上动手进行验证
//因L(1 ≤ L ≤ 2,000,000,000)  f = 9 * L / gcd(8, L); 最大值f时,L=1999999999,最大值f=9*1999999999/1=9*1999999999=17999999991
//故快速幂运算时,模运算参与其中,计算过程中产生的最大值 17999999991*10,000,000,000=1.8*10^20
//而long long 最大值2^63-1=9.2*10^18 unsigned long long 最大值 2^64-1=1.8*10^19
//经过上述一番计算,弄明白了为什么long long 乃至unsigned long long 也会溢出,为学习新知识,做了铺垫
//第一次知道了,快速幂也可以优化。
//优化考的是快速乘,马上学习快速乘

//此文快速乘介绍得不错,http://blog.csdn.net/thearcticocean/article/details/50432502摘抄如下:

//此文快速乘介绍得不错,http://blog.csdn.net/u014630623/article/details/50302365 摘抄如下:

//条件具备,可以开始动手编写代码了 2018-2-20 13:23
//该题核心算法:快速排序+欧拉函数+快速幂+快速乘
//约数个数计算,一个正数N的约数上界为2*sqrt(n),欧拉函数最大值,phi[i]=i-1,若i为质数
//故约数个数2*sqrt(17999999991-1)=268329;故约数数组只需开到268400即可,即factor[268400];
//该题所需具备知识:质数,约数,欧几里德算法,欧拉函数,同余,欧拉定理
//编写完毕后,样例未通过,排查,漏洞百出,详见程序,写代码的过程中要十分小心,稍有不慎long long溢出,
//样例通过后,提交,Time Limit Exceed
//对比了该题与 POJ-3696 发现 该题 L(1≤ L ≤ 10^12)而 POJ-3696 L(1 ≤ L ≤ 2,000,000,000) 数据范围竟然改了
//factor范围重新计算,2*sqrt(9*10^12)=2*3*10^6;
//用原书提供的代码提交,Wrong Answer ,那么好,此题作罢,只要POJ-3696上能通过,即可
//推测,该题原文需要修改 与原题 一致,该题数据也需重新修改。该题就此作罢2018-2-20 16:54
#include <stdio.h>
#define LL long long
LL q,factor[268400];
LL gcd(LL a,LL b){//欧几里德算法
    return b?gcd(b,a%b):a;//此处写成return b?gcd(b,b%a):a; 欧几里德算法都搞晕了
}
LL phi(LL n){//欧拉函数
    LL ans=n,i;
    for(i=2;i*i<=n;i++)
        if(n%i==0){
            ans=ans/i*(i-1);//此处写成 ans=ans*(i-1)/i;计算过程中long long 会溢出
            while(n%i==0)n/=i;
        }
    if(n>1)ans=ans/n*(n-1);//此处写成 ans=ans*(n-1)/n;计算过程中long long 会溢出
    return ans;
}
LL quick_mul(LL a,LL b){//快速乘,因快速幂计算过程中,long long 要溢出,采用 快速乘 优化
    LL ans=0;
    while(b){
        if(b&1)ans=(ans+a)%q;
        a=(a+a)%q,b>>=1;
    }
    return ans;
}
LL quick_pow(LL a,LL b){//快速幂
    LL ans=1;
    while(b){
        if(b&1)ans=quick_mul(ans,a);
        a=quick_mul(a,a),b>>=1;
    }
    return ans;
}
void quick_sort(LL left,LL right){//快速排序 ,自小到大排序
    LL i=left,j=right,mid=factor[(left+right)/2],t;
    while(i<=j){
        while(factor[i]<mid)i++;
        while(factor[j]>mid)j--;
        if(i<=j)t=factor[i],factor[i]=factor[j],factor[j]=t,i++,j--;//i++,j--别忘了,容易漏
    }
    if(i<right)quick_sort(i,right);
    if(left<j)quick_sort(left,j);
}
int main(){
    LL x,p,i,flag,kcase=0;
    while(scanf("%lld",&x)==1&&x){
        q=9*x/gcd(8,x),p=phi(q),factor[0]=0,kcase++,flag=0;//别忘了flag每次都要初始化//factor[0]用来存储约数个数
        for(i=1;i*i<=p;i++)//此处写成for(i=2;i*i<=p;i++),混淆进了质数,1也是约数//找p的约数
            if(p%i==0){
                factor[++factor[0]]=i;
                if(p/i!=i)factor[++factor[0]]=p/i;//另一个约数
            }
        quick_sort(1,factor[0]);
        for(i=1;i<=factor[0];i++)
            if(quick_pow(10,factor[i])%q==1){//此处写成if(quick_pow(10,i)%q==1)继续有失水准//此处写成 if(quick_pow(10,i)%p==1)有失水准
                printf("Case %lld: %lld\n",kcase,factor[i]),flag=1;//此处写成 printf("Case %lld: %lld\n",kcase,i),flag=1;继续有失水准
                break;
            }
        if(flag==0)printf("Case %lld: 0\n",kcase);
    }
    return 0;
}

//4705: Sumdiv
//https://vjudge.net/problem/POJ-1845可见原题
//该题最大的难点 弄懂 乘法逆元
//概念好懂,推理难懂
//关于费马小定理,此文介绍得不错http://www.xieguofang.cn/Maths/Number_Theory/Fermat's_Little_Theorem_1.htm
//样例通过,提交Wrong Answer; 修改了多出的,和少了的c[prime[0]]++,提交 Wrong Answer
//基本上怀疑,有部分地方int溢出,需强转long long 修改,提交Wrong Answer
//ans=ans*(b%mod*c[i]%mod+1)%mod;//此处写成 ans*=ans*(b%mod*c[i]%mod+1)%mod;多次提交,均未发现 修改 提交Wrong Answer
//提交AC后,还经多次测试,明白了那些地方int 会溢出,收获很大2018-2-21 22:27
#include <stdio.h>
#include <string.h>
#define mod 9901
int prime[10],c[10];
void divide(int n){
    int i;
    memset(c,0,sizeof(c)),prime[0]=0;//prime[0]存储质因数个数
    for(i=2;i*i<=n;i++)
        if(n%i==0){
            prime[++prime[0]]=i;//此处多了一句 c[prime[0]]++
            while(n%i==0)n/=i,c[prime[0]]++;//漏了c[prime[0]]++;
        }
    if(n>1)prime[++prime[0]]=n,c[prime[0]]++;//特判,本身n就是质数
}
int quick_pow(int a,int b){
    int ans=1;
    while(b){
        if(b&1)ans=ans*a%mod;
        a=(a%mod)*(a%mod)%mod,b>>=1;//此句 a=(a%mod)*(a%mod)%mod写得不好的情况,容易int溢出 如 a=(a%mod*a%mod)%mod  a=a*a%mod均会溢出
    }
    return ans;
}
int main(){
    int a,b,ans=1,i,x,y;
    scanf("%d%d",&a,&b);
    divide(a);
    for(i=1;i<=prime[0];i++){
        if((prime[i]-1)%mod==0){
            ans=ans*(b%mod*c[i]%mod+1)%mod;//此处写成 ans*=ans*(b%mod*c[i]%mod+1)%mod;多次提交,均未发现
            continue;
        }
        x=quick_pow(prime[i],b*c[i]+1);//请注意 结果有可能 x=0 这是最不容易发现的地方
        x=(x-1+mod)%mod;//最大的Bug 此处写成x=(x-1)%mod; 在计算之前 有可能 x=0//漏了此句 请注意,是等比数列求和之后的 分子 故需 减1
        y=quick_pow(prime[i]-1,mod-2);//prime[i]-1的原因是 等比数列,求和公式计算后的分母就是 prime[i]-1 //此处写成 y=quick_pow(prime[i],mod-2);
        ans=ans%mod*x%mod*y%mod;
    }
    printf("%d",ans);
    return 0;
}
//4795: Noip2012 同余方程
//http://codevs.cn/problem/1200/可见该题
//https://www.luogu.org/problemnew/show/P1082可见该题

//样例通过,提交AC 2018-2-22 16:05

//http://blog.csdn.net/yoer77/article/details/69568676扩展欧几里德算法,此文介绍得不错,读者可以研读

#include <stdio.h>
int exgcd(int a,int b,int *x,int *y){
    int d,t;
    if(b==0){
        *x=1,*y=0;
        return a;
    }
    d=exgcd(b,a%b,x,y);//此处写成 d=exgcd(a,b,x,y); 确无递推关系
    t=*x,*x=*y,*y=t-a/b**y;
    return d;
}
int main(){
    int a,b,x,y;
    scanf("%d%d",&a,&b);
    exgcd(a,b,&x,&y);
    printf("%d",(x+b)%b);
    return 0;
}
//4796: Strange Way to Express Integers
//https://vjudge.net/problem/POJ-2891可见原题
//https://www.cnblogs.com/L-King/p/5721035.html此文代码比较对路
//https://www.cnblogs.com/Missa/archive/2013/06/01/3112536.html
//http://blog.csdn.net/u012294939/article/details/44040091思路摘抄如下(有删改):
//思路,可以参看上述两文,交替研究
//同样是求这个东西。。
//X mod m1=r1
//X mod m2=r2
//...
//...
//...
//X mod mn=rn
//首先,我们看两个式子的情况
//X mod m1=r1……………………………………………………………(1)
//X mod m2=r2……………………………………………………………(2)
//则有
//X=m1*k1+r1………………………………………………………………(*)
//X=m2*k2+r2
//那么 m1*k1+r1=m2*k2+r2
//整理,得
//m1*k1-m2*k2=r2-r1
//令(a,b,x,y,m)=(m1,m2,k1,k2,r2-r1),原式变成
//ax+by=m
//熟悉吧?
//此时,因为GCD(a,b)=1不一定成立,GCD(a,b) | m 也就不一定成立。所以应该先判 若 GCD(a,b) | m 不成立,则!!!方程无解!!!。
//否则,继续往下。
//解出(x0,y0),将k1=x0反代回(*),得到X。即X0%m1=r1,X0%m2=r2
//即X=x0,通解可写成X=x0+m1*k1,X=x0+m2*k2 即(x0+m1*k1)%m1=x0%m1=r1, (x0+m2*k2)%m2=x0%m2=r2
//综合 X=x0+m1*k1,X=x0+m2*k2 可得通解 X=x0+k*LCM(m1,m2) 注LCm(m1,m2)是m1,m2的最小公倍数
//于是X就是这两个方程的一个特解,通解就是 X'=X0+k*LCM(m1,m2)
//这个式子再一变形,得 X' mod LCM(m1,m2)=X0,请注意等号右边是x0
//这个方程一出来,说明我们实现了(1)(2)两个方程的合并。
//令 M=LCM(m1,m2),x0
//就可将合并后的方程记为 X mod M = x0。
//然后,扩展到n个方程。
//用合并后的方程再来和其他的方程按这样的方式进行合并,最后就能只剩下一个方程 X mod M=X0,其中 M=LCM(m1,m2,...,mn)。
//那么,X便是原模线性方程组的一个特解,通解为 X'=X+k*M。
//如果,要得到X的最小正整数解,就还是原来那个方法:
//X%=M;
//if (X<0) X+=M;
//研究下来,发现http://blog.csdn.net/u012294939/article/details/44040091代码更易看懂
//样例通过,提交Wrong Answer
//排查,发现if(f==0)printf("-1\n");//此处写成 printf("0\n"); 真是昏招尽出
//提交AC,2018-2-38 14:03
#include <stdio.h>
#define LL long long
LL exgcd(LL a,LL b,LL *x,LL *y){//每写一次拓展欧几里德算法,就要对该公式进行一次纸上推导
    LL d,t;
    if(b==0){//结束条件
        *x=1,*y=0;
        return a;
    }
    d=exgcd(b,a%b,x,y);
    t=*x,*x=*y,*y=t-a/b**y;
    return d;
}
int main(){
    LL n,a1,r1,a2,r2,x,y,i,f,d,t;
    while(scanf("%lld",&n)!=EOF){
        f=1;
        scanf("%lld%lld",&a1,&r1);
        for(i=2;i<=n;i++){
            scanf("%lld%lld",&a2,&r2);
            if(f==1){
                d=exgcd(a1,a2,&x,&y);
                if((r2-r1)%d){
                    f=0;
                    continue;
                }
                x=(r2-r1)/d*x,t=a2/d;//此处写成t=a2/(r2-r1)//每次到这里,总要对ax+by=c进行推导
                x=(x%t+t)%t;//要求x0>0
                r1=a1*x+r1,a1=a1/d*a2;//a1=a1/d*a2 即a1=(a1*a2)/d;
            }
        }
        if(f==0)printf("-1\n");//此处写成 printf("0\n"); 真是昏招尽出
        else printf("%lld\n",r1);
    }
    return 0;
}
 

0x34 矩阵乘法

//4797: Poj3070 Fibonacci
//https://vjudge.net/problem/POJ-3070可见原题
//原以为,矩阵乘法挺简单,曾经学过,
//没想到有如此妙用,能快速计算该题
//也没想到快速幂还能针对矩阵乘
//真是大开眼界
//样例通过,提交AC 2018-2-23 19:40
#include <stdio.h>
#include <string.h>
#define LL long long
#define m 10000
LL f[2],a[2][2],c1[2],c2[2][2];
void mul(LL f[2],LL a[2][2]){//1*2矩阵 * 2*2矩阵
     LL i,j;
     memset(c1,0,sizeof(c1));
     for(i=0;i<2;i++)
        for(j=0;j<2;j++)
            c1[i]+=f[j]*a[j][i]%m;//该处i,j如何放置,只要手动能模拟出,就能写出该程序
     memcpy(f,c1,sizeof(c1));
}
void mul2(LL a[2][2]){//2*2矩阵 * 2*2矩阵
    LL i,j,k;
    memset(c2,0,sizeof(c2));
    for(i=0;i<2;i++)
        for(j=0;j<2;j++)
            for(k=0;k<2;k++)
                c2[i][j]+=a[i][k]*a[k][j]%m;//同样,该处的i,j,k如何放置,只要手动能模拟出,就能写出该程序
    memcpy(a,c2,sizeof(c2));
}
int main(){
    LL n;
    while(scanf("%lld",&n)&&n!=-1){
        f[0]=0,f[1]=1,a[0][0]=0,a[0][1]=1,a[1][0]=1,a[1][1]=1;
        if(n==0)printf("0\n");
        else if(n==1)printf("1\n");
        else{//n>=2
            n-=1;
            while(n){//快速幂
                if(n&1)mul(f,a);
                mul2(a);
                n>>=1;
            }
            printf("%lld\n",f[1]%m);
        }
    }
    return 0;
}
//4798: Lydsy2973 石头游戏
//弄不懂样例啊,无奈之下,https://www.cnblogs.com/CQzhangyu/p/6791205.html运行提供的代码,对样例进行测试
//测试过程中,突然明白样例所表达的意思,解释如下
//1 6 10 3 1行6列 6个格子  10秒 3个操作序列
//011112 0格子对应0操作序列,1格子对应1操作序列,2格子对应1操作序列,3格子对应1操作序列,4格子对应1操作序列,2格子对应2操作序列
//1E 0操作序列   第1s 产生1个石头,第2s 对应格子的石头往右移 依次类推
//E  1操作序列   第1s 对应格子的石头往右移 依次类推
//0  2操作序列   第1s 产生0个石头 依次类推

//过程如下
//第1s 0格子里有1个石头
//第2s 0格子里的这个头移到1格
//第3s 0格子里有1个石头,1格里的石头移到2格

//样例总算弄明白了。

//该题陌生度还是比较大,简单看了书中的思路,一句话,都没想到
//对照书中思路,开始编码
//矩阵乘法+快速幂
//为了帮组大家理解上述算法,对样例进行了模拟,过程如下: 

//仔细思考,发现该句无法处理,“D:拿走这个格子的石头。”
//https://www.cnblogs.com/CQzhangyu/p/6791205.html再扫一遍里面的代码,发现“D:拿走这个格子的石头。”这句确没处理
//提交上述代码,AC,确实没有处理“D:拿走这个格子的石头。” ,建议该题删去这句“D:拿走这个格子的石头。”,否则该题很难处理
//if('0'<=cmd&&cmd<='9')a[k][num[i][j]][num[i][j]]=1,a[k][0][num[i][j]]=cmd-'0';//程序最难编写的地方//此处写成if('0'<=cmd&&cmd<='9')a[k][0][num[i][j]]=cmd-'0';
//样例通过,提交,Runtime Error
//char s[15][10],c[100];//此处写成c[10] 应是n*m  提交,还是 Runtime Error
//int i,j,k,len,p;//此处写成int i,j,k,len,t; 造成与外面的全局变量t冲突 ,提交,还是 Runtime Error
//多次修改无果,无奈,只好翻出该书提供的测试数据
//看了测试数据后,才发现,“接下来n行,每行m个字符,表示每个格子对应的操作序列。”把n行m列 读成了1行
//修改,提交AC 2018-2-26 20:14 该题编的好久,持续3天。
//提供一组输入数据
//输入:
8 8 390 6
51111112
11111122
41111222
44112222
44403222
44433322
44333332
43333333
000000
00000E
00000S
00000W
00000N
10000E
//输出:
 3
#include <stdio.h>
#include <string.h>
#define LL long long
int n,m,t,act,q,r;
int num[10][10];//num[i][j]对应格子位置,
LL f[100],a[100][100][100],d[100][100],b[100];//f[num[i][j]]记录格子(i,j)中石头个数
char s[15][10],c[10][10]; //此处写成c[100]//此处写成c[10] 应是n*m
void push(int i,int j,int k,char cmd){//产生石头 ,以及 往四周推 石头
    if('0'<=cmd&&cmd<='9')a[k][num[i][j]][num[i][j]]=1,a[k][0][num[i][j]]=cmd-'0';//程序最难编写的地方//此处写成if('0'<=cmd&&cmd<='9')a[k][0][num[i][j]]=cmd-'0';
    else if(cmd=='N'&&i>1)a[k][num[i][j]][num[i-1][j]]=1;
    else if(cmd=='S'&&i<n)a[k][num[i][j]][num[i+1][j]]=1;
    else if(cmd=='W'&&j>1)a[k][num[i][j]][num[i][j-1]]=1;
    else if(cmd=='E'&&j<m)a[k][num[i][j]][num[i][j+1]]=1;
}
void mul(LL a[100][100],LL b[100][100]){//矩阵乘法
    int i,j,k;
    memset(d,0,sizeof(d));
    for(i=0;i<=n*m;i++)
        for(j=0;j<=n*m;j++)
            for(k=0;k<=n*m;k++)
                d[i][j]+=a[i][k]*b[k][j];
    memcpy(a,d,sizeof(d));
}
void mul2(LL f[100],LL a[100][100]){//矩阵乘法
    int i,j;
    memset(b,0,sizeof(b));
    for(i=0;i<=n*m;i++)
        for(j=0;j<=n*m;j++)
            b[i]+=f[j]*a[j][i];
    memcpy(f,b,sizeof(b));
}
void quick_pow(LL a[100][100],LL b[100][100],int q){//快速幂
    while(q){
        if(q&1)mul(b,a);
        mul(a,a),q>>=1;
    }
}
int main(){
    int i,j,k,len,p;//此处写成int i,j,k,len,t; 造成与外面的全局变量t冲突//t=q*60+r
    LL max=-1;
    memset(f,0,sizeof(f)),f[0]=1;//f[i]状态矩阵
    memset(a,0,sizeof(a)),a[0][0][0]=1;//a[i][j]转移矩阵
    scanf("%d%d%d%d",&n,&m,&t,&act);
    for(i=1;i<=n;i++)scanf("%s",c[i]);//此处写成scanf("%s",c);
    for(i=0;i<act;i++)scanf("%s",s[i]);
    q=t/60,r=t%60;
    for(i=1;i<=n;i++)//行
        for(j=1;j<=m;j++)//列
            num[i][j]=(i-1)*m+j;//二维化一维
    for(k=1;k<=60;k++){//经测试无误//60是1-6的最小公倍数 处理a1-a60 转移矩阵
        a[k][0][0]=1;//该位置产生石头
        for(i=1;i<=n;i++)
            for(j=1;j<=m;j++){
                p=c[i][j-1]-'0';//此处写成t=c[num[i][j]]-'0';//(i,j)格子对应的操作序列
                len=strlen(s[p]);
                push(i,j,k,s[p][(k-1)%len]);//此处写成 for(p=0;p<len;p++)push(i,j,k,s[t][p]);
            }
    }
    memcpy(a[61],a[1],sizeof(a[1]));//a[61] 用来存储ar转移矩阵
    for(k=2;k<=r;k++)mul(a[61],a[k]);//计算ar转移矩阵
    memcpy(a[62],a[61],sizeof(a[61]));//a[62] 用来存储a60转移矩阵
    for(k=r+1;k<=60;k++)mul(a[62],a[k]);//此处写成for(k=r+1;k<=60;k++)mul(a[61],a[k]);昏招//计算a60转移矩阵
    for(i=0;i<=n*m;i++)//将a[63]初始化为单位矩阵
        for(j=0;j<=n*m;j++)
            if(i==j)a[63][i][j]=1;
    quick_pow(a[62],a[63],q);//a[63]用来存储(a60)^q
    mul2(f,a[63]);
    mul2(f,a[61]);
    for(i=1;i<=n*m;i++)
        if(max<f[i])max=f[i];
    printf("%lld",max);
    return 0;
}

0x35 高斯消元法与线性空间

//球形空间产生器BZOJ1013
//http://www.lydsy.com/JudgeOnline/problem.php?id=1013可见原题
//https://www.luogu.org/problemnew/show/P4035可见原题
//解释一下,为什么要提供n+1组数据,圆心坐标x1,x2,...,xn-1,xn,有n个未知数,半径R也是一个未知数,未知数共计n+1个,需要n+1个方程,故需提供n+1组数据
//对样例进行推演
//(a11-x1)^2+(a12-x2)^2=R^2  (1)   a11表述数据1在1维度上的坐标值,a12表述数据1在2维度上的坐标值
//(a21-x1)^2+(a22-x2)^2=R^2  (2)   a21表述数据2在1维度上的坐标值,a22表述数据2在2维度上的坐标值
//(a31-x1)^2+(a32-x2)^2=R^2  (3)   a31表述数据3在1维度上的坐标值,a32表述数据3在2维度上的坐标值
//将(2)-(1)可得   2*(a11-a21)*x1+2*(a12-a22)*x2=a11^2-a21^2+a12^2-a22^2
//将(3)-(2)可得   2*(a21-a31)*x1+2*(a22-a32)*x2=a21^2-a31^2+a22^2-a32^2

//上述样例推演目的,方便读者看懂书中通式

//惊呆了,样例一次性通过,洛谷里 提交,一次性AC。2018-2-27 22:35
//还是那句话,会算,编起来就容易多了
#include <stdio.h>
#include <string.h>
#include <math.h>
double a[15][15],b[15],c[15][15];
void swap(double a,double b){
    double t;
    t=a,a=b,b=t;
}
int main(){
    int i,j,k,n;
    double rate;
    memset(b,0,sizeof(b));
    scanf("%d",&n);
    for(i=1;i<=n+1;i++)//数据读取
        for(j=1;j<=n;j++)
            scanf("%lf",&a[i][j]);
    for(i=1;i<=n;i++)//转化为 行列式
        for(j=1;j<=n;j++)
            c[i][j]=2*(a[i][j]-a[i+1][j]),b[i]+=a[i][j]*a[i][j]-a[i+1][j]*a[i+1][j];
    for(i=1;i<=n;i++){//i表示矩阵的列//高斯消元
        for(j=i;j<=n;j++){//该功能保证当前的x[i]系数非零//j表示矩阵的行
            if(fabs(c[j][i])>1e-8){//保证x[i]系数非零//非零
                for(k=1;k<=n;k++)swap(c[i][k],c[j][k]);
                swap(b[i],b[j]);
            }
        }
        for(j=1;j<=n;j++){//所有的行//目标:除了对角线外,其他列处的系数为0
            if(j==i)continue;//避免出现自己-自己 的情况
            rate=c[j][i]/c[i][i];
            for(k=1;k<=n;k++)c[j][k]-=c[i][k]*rate;//每行的数据
            b[j]-=b[i]*rate;
        }
    }
    printf("%.3lf",b[1]/c[1][1]);
    for(i=2;i<=n;i++)printf(" %.3lf",b[i]/c[i][i]);//打印
    return 0;
}
//4800: Poj1830 开关问题
//https://vjudge.net/problem/POJ-1830可见原题
//开关,容易与二进制搭上关系
//0 < N < 29 ,故0 < N+1 < 30  int有31位,极限情况int还是不会溢出
//样例通过,提交Wrong Answer
//ans=1;//漏了此句,if(a[i]==1),if(a[i]==0)均未进入,即自由元0个,2^0=1答案为1 ,因为此句,一直Wrong Answer
//提交AC,2018-3-1 14:51
#include <stdio.h>
int a[40];
int main(){
    int i,j,k,t,n,x,y,ans,b;
    scanf("%d",&t);    
    while(t--){
        ans=1;//漏了此句,if(a[i]==1),if(a[i]==0)均未进入,即自由元0个,2^0=1答案为1 ,因为此句,一直Wrong Answer
        scanf("%d",&n);
        for(i=1;i<=n;i++)scanf("%d",&a[i]);
        for(i=1;i<=n;i++){
            scanf("%d",&j);
            a[i]^=j,a[i]|=1<<i;//处理第0位,第i位
        }
        while(~scanf("%d%d",&x,&y)&&x&&y)a[y]|=1<<x;
        for(i=1;i<=n;i++){
            for(j=i+1;j<=n;j++)//此处写成for(j=i+1;j<=n;i++)笔误害人//类冒泡排序
                if(a[i]<a[j])//自大到小排序,即找出最高位
                    b=a[i],a[i]=a[j],a[j]=b;//此处写成 t=a[i],a[i]=a[j],a[j]=t;请注意t已被 数据组数所占用
            if(a[i]==1){ans=0;break;}//即 0001 无解
            if(a[i]==0){ans=1<<n-(i-1);break;}//即a[i]==0,a[i+1]==0,...,a[n]==0 0000 即i ,i+1,...,n均是自由元
            for(k=n;k>=1;k--)//高斯消元
                if(a[i]>>k&1){//找到最高位为1
                    for(j=1;j<=n;j++)//削掉第k位
                        if(j!=i&&a[j]>>k&1)a[j]^=a[i];//该行if(j!=i&&a[j]>>k&1)中的a[j]>>k&1翻书才想起
                    break;
                }       
        }
        if(ans==0)printf("Oh,it's impossible~!!\n");
        else printf("%d\n",ans);
    }
    return 0;
}

//4801: Lydsy4004 装备购买
//https://www.luogu.org/problemnew/show/P3265可见该题,网络中的代码在此处大都能AC
//http://begin.lydsy.com/JudgeOnline/problemstatus.php?id=4801这里的数据更苛刻,好还是不好呢?
//向量需要高中数学的基础,本质,就是用坐标系表示
//没什么经验,对 秩 感到陌生,弄个代码能AC的代码来研究https://www.cnblogs.com/SilverNebula/p/6353939.html

//贪心+高斯消元

//为了方便读者理解,样例模拟如下:

//经过一番折腾,发现加入long double 之后,Dev-C++4.9.9.2已无跟踪,编译能力,研究的过程瞎子摸象
//建议编写时,这样操作,先用double,在Dev-c++4.9.9.2进行跟踪研究,没有问题后,将double改成long double
//样例通过,终于提交AC,2018-3-2 16:29
//若采用C++方式提交,可用fabs,fabsl,若采用C方式提交,采用fabsl
#include <stdio.h>
#include <string.h>
#include <math.h>
#define double long double//此处写成 #define long double double
#define maxn 510
#define eps 1e-7
struct node{//用结构体的目的是方便排序
    double b[maxn];
    int w;
}a[maxn],a_t,p[maxn];
int vis[maxn];
void quicksort(int left,int right){//快排,按w自小到大排序
    int i=left,j=right,mid=a[(left+right)/2].w;//没想到快排也会写错,此处写成 int i,j,mid=a[(left+right)/2].w;
    while(i<=j){
        while(a[i].w<mid)i++;
        while(a[j].w>mid)j--;
        if(i<=j)a_t=a[i],a[i]=a[j],a[j]=a_t,i++,j--;
    }
    if(i<right)quicksort(i,right);
    if(left<j)quicksort(left,j);
}
int main(){
    int n,m,i,j,k,cnt=0,cost=0;
    double rate;
    memset(vis,0,sizeof(vis));
    scanf("%d%d",&n,&m);
    for(i=1;i<=n;i++)
        for(j=1;j<=m;j++)
            scanf("%Lf",&a[i].b[j]);//请注意long double 必须%Ld  L必须大写
    for(i=1;i<=n;i++)scanf("%d",&a[i].w);
    quicksort(1,n);
    for(i=1;i<=n;i++)//高斯消元
        for(j=1;j<=m;j++)
            if(fabsl(a[i].b[j])>=eps)//排查了math.h文件,发现extern long double __cdecl fabsl (long double x);//此处写成if(fabs(a[i].b[j])>eps) C里面有问题//此处写成if(a[i].b[j]>eps)//大于0
                if(vis[j]==0){//该列元素未被访问
                    vis[j]=1,p[j]=a[i],cnt++,cost+=p[j].w;
                    break;//漏了此句
                }
                else{
                    rate=a[i].b[j]/p[j].b[j];
                    for(k=1;k<=m;k++)a[i].b[k]-=rate*p[j].b[k];//此处写成a[i].b[k]-=rate*p[i].b[k];
                }
    printf("%d %d\n",cnt,cost);
    return 0;

}

//4802: Hdu3949 XOR
//http://acm.hdu.edu.cn/showproblem.php?pid=3949可见该题
//https://vjudge.net/problem/HDU-3949可见该题
//提交,Compilation Error,发现全是在有注释的地方,注释竟然不行,
//删除注释,提交Wrong Answer,仔细想了想,zero=0应在每组数据开始 int t,n,zero; //此处写成 int t,n,zero=0;
//修改,提交 Wrong Answer,排查代码,发现if(a[i]==0){t=i-1;zero=1;break}//此处写成 if(a[i]==0)t=i-1,zero=1;

//修改,提交Wrong Answer,对照代码才发现,本应是unsigned long long k写成int ,题目没读全,修改,提交AC 2018-3-3 12:28

//if(zero)k--;原因是,利用异或得不到0,最小值0必须从ans=0的初始值中得到,故必须k--

#include <stdio.h>
#define maxn 10100
#define ULL unsigned long long
ULL a[maxn],b;
int t,n,zero;
void gauss(){
    int i,j,k;
    for(i=1;i<=n;i++){
        for(j=i+1;j<=n;j++)
            if(a[i]<a[j])b=a[i],a[i]=a[j],a[j]=b;
        if(a[i]==0){t=i-1;zero=1;break;}
        for(k=63;k>=0;k--)
            if(a[i]>>k&1){
                for(j=1;j<=n;j++)
                    if(j!=i&&a[j]>>k&1)
                        a[j]^=a[i];
                break;
            }
    }
}
int main(){
    ULL ans,k;
    int T,q,i,j,kase=0;
    scanf("%d",&T);
    while(T--){
        scanf("%d",&n);
        for(i=1;i<=n;i++)scanf("%llu",&a[i]);
        t=n,zero=0;
        gauss();
        scanf("%d",&q);
        kase++;
        printf("Case #%d:\n",kase);
        while(q--){
            ans=0;
            scanf("%llu",&k);
            if(zero)k--;
            if(k>=1ULL<<t){printf("-1\n");continue;}
            for(i=t-1;i>=0;i--)
                if(k>>i&1)ans^=a[t-i];
            printf("%llu\n",ans);
        }
        
    }
    return 0;
}

0x36 组合计数

//4803: Noip2011 计算系数
//http://codevs.cn/problem/1137/可见该题
//https://www.luogu.org/problemnew/show/P1313可见该题
//由杨辉三角可得:C(k,n)=C(k-1,n-1)+c(k-1,n) 初始值C(k,0)=1,C(k,k)=1
//因存在int溢出风险,故全采用long long
//样例通过,提交AC 2018-3-3 21:28
//方法二:快速幂+逆元
//参考了https://www.luogu.org/problemnew/solution/P1313?&page=3写法,看来逆元需要好好复习2018-3-3 21:46
#include <stdio.h>
#define mod 10007
#define LL long long
LL quick_pow(LL a,LL n){//快速幂
    LL ans=1;
    while(n){
        if(n&1)ans=(ans*a)%mod;
        a=(a*a)%mod;
        n>>=1;
    }
    return ans;
}
int main(){
    LL a,b,k,n,m,i,j,c=1,d=1;
    scanf("%lld%lld%lld%lld%lld",&a,&b,&k,&n,&m);
    for(i=1;i<=n;i++)c=c*i%mod;
    for(i=k-n+1;i<=k;i++)d=d*i%mod;
    d=d*quick_pow(c,mod-2)%mod;
    printf("%lld",d*quick_pow(a,n)*quick_pow(b,m)%mod);
    return 0;
}

//4803: Noip2011 计算系数
//http://codevs.cn/problem/1137/可见该题
//https://www.luogu.org/problemnew/show/P1313可见该题
//由杨辉三角可得:C(k,n)=C(k-1,n-1)+c(k-1,n) 初始值C(k,0)=1,C(k,k)=1
//因存在int溢出风险,故全采用long long
//样例通过,提交AC 2018-3-3 21:28
#include <stdio.h>
#define mod 10007
#define LL long long
LL c[1010][1010];
LL quick_pow(LL a,LL n){//快速幂
    LL ans=1;
    while(n){
        if(n&1)ans=(ans*a)%mod;
        a=(a*a)%mod;
        n>>=1;
    }
    return ans;
}
int main(){
    LL a,b,k,n,m,i,j;
    scanf("%lld%lld%lld%lld%lld",&a,&b,&k,&n,&m);
    for(i=1;i<=k;i++)c[i][0]=1;
    for(i=1;i<=k;i++)c[i][i]=1;
    for(i=2;i<=k;i++)
        for(j=1;j<i;j++)
            c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
    printf("%lld",(c[k][n]*quick_pow(a,n)*quick_pow(b,m))%mod);
    return 0;

}

多重集合的组合数 可通过 https://blog.csdn.net/mrcrack/article/details/80562324 第10讲内容 内容进行 学习。2018-9-8 19:07

多重集的组合数:http://blog.csdn.net/kennyrose/article/details/7469528此文介绍得相当棒,摘抄其中一个例子如下:

 

例1:线性方程 x1 + x2 + ... + xk = r 一共有多少组非负整数解?

解答:上述不定方程的非负整数解对应于下述排列

1...101...1 01...1 0 ...... 01...1

x1 个    x2 个   x3 个   ......  xk 个

其中 k-1个 0 将 r 个 1 分成k段, 每段含1的个数分别为 x1, x2, ..., xk, 

很明显这个排列是多重集合 S = { r * 1, (k-1)* 0 }的全排列

即:P(r+k-1; r*1, (k-1)*0) = (r+k-1)! / ( r! * (k-1)! ) = C( r+k-1, r),即从k类元素中选r个的种类

2018-3-9 22:08

//4804: Counting Swaps

//https://ipsc.ksp.sk/2016/real/problems/c.html可见该题

//因无处提交代码,此题暂时搁置。2018-3-5

//4805: Lydsy1951 古代猪文
//http://codevs.cn/problem/1830/可见该题
//https://www.luogu.org/problemnew/show/P2480可见该题
//样例模拟如下
//N=4,q=2,d|N d=1,4,2
//C(4,1)=4,C(4,2)=6,C(4,4)=1 和为11
//2^11=2048
//https://www.cnblogs.com/wuminyan/p/5188551.html此文代码写得不错,可以参考一下
//光看他人源代码,就花了很长时间,有3天之多,感受是思维提升了,很多数论问题有了更深的体会。
//乘法逆元+卢卡斯定理+拓展欧几里得算法+中国剩余定理+快速幂
//为尽可能的避免数据溢出,采用long long  
//该题对数论要求比较高,要想编码成功,需要对数论比较清晰。样例通过,提交AC 2018-3-9
//该题耗时五天之久,收获就是数论有些感觉。
#include <stdio.h>
#define LL long long
#define mod 999911659
LL n,g,fact[4][35700],prime[4]={2,3,4679,35617},b[4];//此处写成fact[35700]//fact阶乘 prime[] 2*3*4679*35617=999911658
LL quick_pow(LL a,LL b,LL x){
    LL ans=1;
    while(b){
        if(b&1)ans=ans*a%x;
        a=a*a%x,b>>=1;
    }
    return ans;
}
LL Comb(LL n,LL m,LL i){//组合数计算
    if(n<m)return 0;//为了程序的健壮性写的
    return fact[i][n]*quick_pow(fact[i][n-m]*fact[i][m],prime[i]-2,prime[i])%prime[i];//此处写成fact[i][n]*quick_pow(fact[i][n-m]*fact[i][m],prime[i]-2,i)%prime[i];//乘法逆元
}
LL lucas(LL n,LL m,LL i){
    if(m==0)return 1;
    return Comb(n%prime[i],m%prime[i],i)*lucas(n/prime[i],m/prime[i],i)%prime[i];
}
LL exgcd(LL a,LL b,LL *x,LL *y){
    LL t,d;
    if(b==0){
        *x=1,*y=0;
        return a;
    }
    d=exgcd(b,a%b,x,y);//此处写成 d=exgcd(a,b,x,y);
    t=*x,*x=*y,*y=t-a/b**y;
    return d;
}
LL CRT(){//中国剩余定理
    LL N=1,i,j,m,ans=0,x,y;
    for(i=0;i<4;i++)N*=prime[i];
    for(i=0;i<4;i++){
        m=N/prime[i];
        exgcd(m,prime[i],&x,&y);
        ans=(ans+x*m*b[i])%N;//此处写成ans=(ans+x*m*b[i])%mod;,查了好长时间
    }
    ans=(ans%N+N)%N;//此处写成ans=(ans%mod+N)%mod;,查了好长时间 //保证ans最后结果>0
    return ans;
}
int main(){
    LL i,j,k;
    scanf("%lld%lld",&n,&g);
    if(g==mod){
        printf("0");
        return 0;
    }
    g%=mod;
    for(i=0;i<4;i++){
        fact[i][0]=1;
        for(j=1;j<=prime[i];j++)
            fact[i][j]=fact[i][j-1]*j%prime[i];//此处写成fact[i][j]=fact[i][j-1]*j%mod;//fact[i]=i!阶乘计算
    }
    for(i=1;i*i<=n;i++)//找n的约数
        if(n%i==0){
            k=n/i;
            for(j=0;j<4;j++){
                if(k!=i)b[j]=(b[j]+lucas(n,k,j))%prime[j];
                b[j]=(b[j]+lucas(n,i,j))%prime[j];
            }
        }
    printf("%lld",quick_pow(g,CRT(),mod)%mod);
    return 0;
}
 

0x37 容斥原理与Mobius函数

容斥原理,多重集的组合数,可看https://blog.csdn.net/mrcrack/article/details/80562324 第17讲内容 例子 10组合,定能弄明白书中公式的意义。2018-9-8 20:14 

没有接触过容斥原理,可以看看这篇文章http://www.sohu.com/a/198865515_128501先有一个基本印象。2018-3-10 10:21

关于多重集,此文介绍得相当棒https://blog.csdn.net/kennyrose/article/details/7469528摘抄部分内容如下       2018-4-20

例1:线性方程 x1 + x2 + ... + xk = r 一共有多少组非负整数解?

解答:上述不定方程的非负整数解对应于下述排列

1...101...1 01...1 0 ...... 01...1

x1 个    x2 个   x3 个   ......  xk 个

其中 k-1个 0 将 r 个 1 分成k段, 每段含1的个数分别为 x1, x2, ..., xk, 

很明显这个排列是多重集合 S = { r * 1, (k-1)* 0 }的全排列

即:P(r+k-1; r*1, (k-1)*0) = (r+k-1)! / ( r! * (k-1)! ) = C( r+k-1, r),即从k类元素中选r个的种类

多重集的理解,借助上例,很快就能理解。

多重集的例题,此文不错,可以学习研究https://blog.csdn.net/thearcticocean/article/details/46794101

例题 Devu and Flowers 在线测评可见 https://vjudge.net/problem/CodeForces-451E

试着推导 多重集的组合数公式,竟然成功了,方法一是从 公式角度入手,方法二是从组合的意义入手,全部成功。虽然有些磕碰,但时隔这么久,能成功,证明是掌握了。2018-9-9 16:15

根据容斥原理,将样例全部算了一遍,数据全部对上,可以开始考虑编码了。2018-9-9 16:36

看了数据范围,long long是省不了了,有顾虑,算组合数,十有八九要溢出。2018-9-9 16:48

还有一个疑惑,容斥原理如何结束。2018-9-9 17:05

//https://vjudge.net/problem/CodeForces-451E
//Devu and Flowers
//参考了书中代码,但没有照抄。
//编好一个函数,测试一个函数
//2018-9-11
//t-=p;//此处写成t-=(p+1); 这是编码中最核心的错误,大约花了30分钟排错 主要靠printf 才找到错误
//3个样例均通过,提交AC。2018-9-11
//该程序的原理,多重集的组合+容斥原理 的学习,致使该代码的诞生足足延迟了半年。
#include <stdio.h>
#define LL long long
#define mod 1000000007
LL inv[25];//逆元
LL myPower(LL a,LL b){//a^b 快速幂 可采用2^10模拟 来编写该代码。
    LL c=1;
    while(b){
        if(b&1)//判断b是奇数
            c=c*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return c;
}
LL comb(LL y,LL x){//c(y,x) 想一想C(5,2)计算,该函数很快就能编出。
    LL c=1,i;
    if(y<0||y<x||x<0)return 0;
    if(x==0||y==x)return 1;
    y%=mod;
    for(i=0;i<x;i++)c=c*(y-i)%mod;
    for(i=x;i>=1;i--)c=c*inv[i]%mod;//变除法为乘法 逆元
    return c;
}
int main(){
    LL i,a[25],n,s,ans,p,t,x;
    for(i=1;i<=20;i++)inv[i]=myPower(i,mod-2);//计算i的逆元
    scanf("%lld%lld",&n,&s);
    for(i=1;i<=n;i++)scanf("%lld",&a[i]);
    for(x=0;x<1<<n;x++){//二进制 每位对应一个集合的使用 多重集的组合+容斥原理 该循环,测试了x的值,一次性成功。
        p=0;
        t=n+s;
        if(x==0)ans=comb(t-1,n-1);
        else{
            for(i=0;i<n;i++)//查找n次,因有n位
                if(x>>i&1){
                    p++;
                    t-=a[i+1];
                }
            t-=p;//此处写成t-=(p+1); 这是编码中最核心的错误,大约花了30分钟排错 主要靠printf 才找到错误//之前的问题 将该代码块 放在了 else之外。
            if(p&1){//奇数 -
                ans=(ans-comb(t-1,n-1))%mod;
            }else{//偶数 +
                ans=(ans+comb(t-1,n-1))%mod;
            }
        }
    }
    printf("%lld\n",(ans+mod)%mod);
}

 

0x38 概率与数学期望

 

0x39 0/1分数规划

 

0x3A 博弈论之SG函数

 

0x3B 总结与练习

 

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值