math_6

45 篇文章 0 订阅
7 篇文章 0 订阅

math_6


巨胖的技能组合


【问题描述】

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


【输入】

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


【输出】

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


【输入样例】

2 0 3


【输出样例】

4


【样例解释】

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


【数据范围】

对于 20% 的数据,保证有 N M5
对于 50% 的数据,保证有 N M100
对于 80% 的数据,保证有 N M1000
对于 100% 的数据,保证有 N M105 KMin(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 long

using 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值