math_6
巨胖的技能组合
【问题描述】
巨胖是打DNF的高手。高手中的高手。
巨胖有
N
种技能,他
其中有
由于巨胖的技术、装备、人品均为一流,所以其他技能都可以无限制释放。
对于两个巨胖一分钟内释放
M
次技能的方案,若存在一个技能使得这两个方案中这个技能的使用次数不同,那么这两个方案视为不同的方案。
巨胖为了追求潮炫酷,想知道不同的技能释放方案有多少。
【输入】
输入文件名为Dnf.in。
输入第一行三个整数,分别为
输入第二行为
K
个正整数,代表那
【输出】
输出文件名为Dnf.out。
输出一行一个正整数,为不同的技能释放方案总数对
1000000007
的模值。
【输入样例】
2 0 3
【输出样例】
4
【样例解释】
不同的方案为
4
种,分别为
【数据范围】
对于
20%
的数据,保证有
N
,
对于
50%
的数据,保证有
N
,
对于
80%
的数据,保证有
N
,
对于
100%
的数据,保证有
N
,
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)
辗转相除的次数定义为
(a,b)
其中有一项变为
0
的时候,产生了多少个不同的数对。
例如:
现在给定一个
对了,当满足条件的
(a,b)
有很多个的时候,选择
a
最小的,若还有很多个,选择
【输入】
输入文件名为Euclid.in。
输入仅一行,一行一个正整数
N
。
【输出】
输出文件名为Euclid.out。
输出包含两行,第一行一个正整数
【输入样例】
4
【输出样例】
2
3
【数据范围】
对于
对于
50%
的数据,
N≤1018
。
对于
100%
的数据,
3≤N≤1012000
。
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。
输出一个正整数
【输入样例】
3
【输出样例】
2
【数据范围】
对于
30%
的数据,保证有
N≤105
。
对于
100%
的数据,保证有
N≤109
。
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;
}