math_6

来源:互联网 发布:剑客软件 编辑:程序博客网 时间:2024/06/08 07:26

math_6


巨胖的技能组合


【问题描述】

巨胖是打DNF的高手。高手中的高手。
巨胖有 N 种技能,他 1 分钟之内可以释放 M 次技能。
其中有 K 种技能,因为无色啊蓝啊CD啊等各种原因,每分钟有一个使用次数上限 Li
由于巨胖的技术、装备、人品均为一流,所以其他技能都可以无限制释放。
对于两个巨胖一分钟内释放 M 次技能的方案,若存在一个技能使得这两个方案中这个技能的使用次数不同,那么这两个方案视为不同的方案。
巨胖为了追求潮炫酷,想知道不同的技能释放方案有多少。


【输入】

输入文件名为Dnf.in。
输入第一行三个整数,分别为 NKM,其表示意义如上所述。
输入第二行为 K 个正整数,代表那 K 种技能一分钟内的使用上限。


【输出】

输出文件名为Dnf.out。
输出一行一个正整数,为不同的技能释放方案总数对 1000000007 的模值。


【输入样例】

2 0 3


【输出样例】

4


【样例解释】

不同的方案为 4 种,分别为 (0,3)(1,2)(2,1)(3,0)


【数据范围】

对于 20% 的数据,保证有 NM5
对于 50% 的数据,保证有 NM100
对于 80% 的数据,保证有 NM1000
对于 100% 的数据,保证有 NM105KMin(N,15)


Solution

容斥原理,同理 硬币购物。
一开始的 DP 可以用组合数替代(打个表就可以发现,其实就是杨辉三角)。


Code

#include <iostream>#include <cstdio>#define MOD 1000000007#define LL long long#define MAXN 200000#define f(x) (C(n+(x)-1,(x),MOD))using namespace std;LL n,k,m,ans;LL need[30];LL ches[30];LL jie[MAXN+10];LL ni[MAXN+10];short mu[]={1,-1,1,-1,1,-1,1,-1,1,-1,1,-1,1,-1,1,-1,1,-1,1,-1,1,-1,1,-1,1,-1,1,-1,1,-1,1,-1,1,-1,1,-1};inline LL C(LL x,LL y,LL p){    if(x<y)return 0;    return jie[x]*ni[x-y]%p*ni[y]%p;}LL power(LL x,LL y,LL p){    if(y==0)return 1;    if(y==1)return x%p;    LL tmp=power(x,y/2,p);    tmp=tmp*tmp%p;    if(y&1)tmp=tmp*(x%p)%p;    return tmp;}void kan(){    LL sum=0;    for(LL i=1;i<=ches[0];i++)sum+=(need[ches[i]]+1);    if(sum>m)return;    ans=((ans+mu[ches[0]]*f(m-sum)%MOD)%MOD+MOD)%MOD;}void dfs(int x){    if(x==k+1){        kan();        return;    }    dfs(x+1);    ches[++ches[0]]=x;    dfs(x+1);    ches[0]--;}int main(){    freopen("dnf.in","r",stdin);    freopen("dnf.out","w",stdout);    jie[0]=ni[0]=1;    for(LL i=1;i<=MAXN;i++)jie[i]=jie[i-1]*i%MOD;    ni[MAXN]=power(jie[MAXN],MOD-2,MOD);    for(LL i=MAXN-1;i>=1;i--)ni[i]=ni[i+1]*(i+1)%MOD;    scanf("%lld%lld%lld",&n,&k,&m);    if(m==0){        printf("1\n");        return 0;    }    for(LL i=1;i<=k;i++)        scanf("%lld",&need[i]);    dfs(1);    printf("%lld\n",ans);    return 0;}

巨胖的辗转相除


【问题描述】

巨胖最近学完了辗转相除法求最大公约数,即欧几里得法求最大公约数之后,非常的开心,尤其是当他发现原来辗转相除法的时间复杂度是 O(logN)的时候,更是喜不自胜。
但是,虽然都是 O(LogN),当 N 给定的时候,一个数对 (a,b)(1abN) 可能只要辗转相除一次就算出最大公约数了,有的可能还要辗转相除若干次。
数对 (a,b) 辗转相除的次数定义为 (a,b) 其中有一项变为 0 的时候,产生了多少个不同的数对。
例如:(3,5)(2,3)(1,2)(0,1),所以数对 (3,5) 辗转相除的次数为 4
现在给定一个 N,求N中间辗转相除次数最多的数对 (a,b)
对了,当满足条件的 (a,b) 有很多个的时候,选择 a 最小的,若还有很多个,选择 b 最小的。


【输入】

输入文件名为Euclid.in。
输入仅一行,一行一个正整数 N


【输出】

输出文件名为Euclid.out。
输出包含两行,第一行一个正整数 a,第二行一个正整数 b


【输入样例】

4


【输出样例】

2
3


【数据范围】

对于 20% 的数据,N104
对于 50% 的数据,N1018
对于 100% 的数据,3N1012000


Solution

打表发现是斐波那契数列……
压位打高精度即可。


Code

#include <iostream>#include <cstdio>#include <cstring>#define LL long long#define MOD 10000000000000000LL;#define Max(x,y) ((x)>(y)?(x):(y))using namespace std;char str[12010];struct bign{    LL s[810],len;    bign(){        memset(s,0,sizeof s);        s[1]=1;        len=1;    }    bign operator = (const char*str){        s[1]=0;        len=(strlen(str)-1)/16+1;        LL yu=strlen(str)%16-1,wei=len;        for(LL i=0;i<=yu;i++)s[wei]=s[wei]*10+str[i]-'0';        for(LL i=yu+1;i<len;i+=16){            --wei;            for(LL j=0;j<16;j++)                s[wei]=s[wei]*10+str[i+j]-'0';        }        return *this;    }    bign operator + (const bign&num){        bign c;        c.s[1]=0;        c.len=Max(num.len,len);        for(LL i=1;i<=c.len;i++){            c.s[i+1]=(c.s[i]+s[i]+num.s[i])/MOD;            c.s[i]=(c.s[i]+s[i]+num.s[i])%MOD;        }        if(c.s[c.len+1])c.len++;        return c;    }    bool operator > (const bign&num)const{        if(len!=num.len)return len>num.len;        for(LL i=len;i>=1;i--)            if(s[i]!=num.s[i])                return s[i]>num.s[i];        return false;    }    void out(){        for(LL i=len;i>=1;i--){            if(i==len)printf("%lld",s[i]);            else printf("%016lld",s[i]);        }    }};bign tmp,a,b,c;int main(){    freopen("euclid.in","r",stdin);    freopen("euclid.out","w",stdout);    scanf("%s",str);    tmp=str;    c=a+b;    while(!(c>tmp)){        a=b;        b=c;        c=a+b;    }    a.out();    putchar(10);    b.out();    return 0;}

巨胖的最大约数


【问题描述】

巨胖虐了无数道现哥出的约数的题目之后,开始对约数感兴趣了。
给定一个范围 [1,N],巨胖想知道范围内的约数最多的数是哪一个。
如果有约数最多的有复数个,则输出数值最小的。


【输入】

输入文件名为Divisor.in。
输入一个正整数 N


【输出】

输出文件名为Divisor.out。
输出一个正整数 X,代表 [1,N] 范围内约数最多且最小的数。


【输入样例】

3


【输出样例】

2


【数据范围】

对于 30% 的数据,保证有 N105
对于 100% 的数据,保证有 N109


Solution

易知答案的唯一分解式中,小的质因子的指数必不小于大的质因子的指数。
暴力搜索即可。


Code

#include <iostream>#include <cstdio>#define LL long longusing namespace std;LL n;LL h[20]={30};LL prime[20],MAXN=-1,ans;bool no_prime[100];void search(LL x,LL prod,LL sum){    LL t=prod,cnt=0;    for(LL i=1;i<=h[x-1];i++){        t=t*prime[x];        if(t>n){            if(MAXN<sum*(cnt+1)||(MAXN==sum*(cnt+1)&&(t/prime[x]<ans))){                MAXN=sum*(cnt+1);                ans=t/prime[x];            }            return;        }        cnt++;        h[x]=i;        search(x+1,t,sum*(cnt+1));    }}int main(){    freopen("divisor.in","r",stdin);    freopen("divisor.out","w",stdout);    for(LL i=2;i<=30;i++){        if(!no_prime[i])prime[++prime[0]]=i;        for(LL j=1;prime[j]*i<=30;j++){            no_prime[i*prime[j]]=true;            if(i%prime[j]==0)break;        }    }    scanf("%lld",&n);    search(1,1,1);    printf("%lld\n",ans);    return 0;}
1 0