看到大佬夺CCPC首金,心潮澎湃的我打算向大佬看齐!
ok,基于在组内分管数学题的任务,我开始学习数学题算法。
第一章,GCD&LCM
这个用辗转相除法代码很容易实现
int gcd(int a,int b)
{
return b==0?a:gcd(b,a%b);
}
int lcm(int a,int b)
{
return a/gcd(a,b)*b;
}
当然我更习惯这样写,还不用考虑a,b的大小
int gcd(int a,int b)
{
if(a<b)swap(a,b);
if(a%b==0)return b;
else return gcd(a%b,b);
}
然后开始刷题!
HDU-1695
emmmm
解析说是要用到莫比乌斯反演,看了一下午没看懂,先放一下
等明天看懂了反演或者试试看容斥原理
莫比乌斯函数要记住结论:
其中莫比乌斯函数的普通筛代码如下:
void getMu()
{
int N=maxn;
for(int i=1;i<N;++i){
int target=i==1?1:0;
int delta=target-mu[i];
mu[i]=delta;
for(int j=2*i;j<N;j+=i)
mu[j]+=delta;
}
}
ac代码如下:
#include<iostream>
#include<cstring>
using namespace std;
int maxn=100005;
int gcd(int a,int b)
{
if(a<b)swap(a,b);
if(a%b==0)return b;
else return gcd(a%b,b);
}
int mu[100050];//莫比乌斯函数值
void getMu()//莫比乌斯函数普通筛
{
int N=maxn;
for(int i=1;i<N;++i)
{
int target;
if(i==1)target=1;
else target=0;
int delta=target-mu[i];
mu[i]=delta;
for(int j=2*i;j<N;j+=i)
mu[j]+=delta;
}
}
int main()
{
getMu();
int a,b,c,d,k;
int t;
cin>>t;
int cntt=1;
while(t--)
{
cin>>a>>b>>c>>d>>k;
if(k==0)
{
cout<<"Case "<<cntt++<<": "<<0<<endl;
continue;
}
long long ans=0;
long long anss=0;
if(b>d)swap(b,d);
b/=k;
d/=k;
for(int i=1;i<=b;i++)
{
ans+=mu[i]*(long long)(b/i)*(d/i);
}
for(int i=1;i<=b;i++)
{
anss+=mu[i]*(long long)(b/i)*(b/i);
}
ans-=anss/2;
cout<<"Case "<<cntt++<<": "<<ans<<endl;
}
}
HDU-1787
题意:找1–N-1中,与N的GCD>1的数的个数
显然欧拉定理
康康欧拉定理的解释,算了!看不懂,直接代码走起
int Euler(int n)
{
int ans=n;
for(int i=2;i*i<=n;i++)
{
if(n%i==0)
{
n/=i;
ans-=ans/i;
}
while(n%i==0)n/=i;
}
if(n>1)ans-=ans/n;
return ans;
}
HDU-2504
水题
#include<iostream>
using namespace std;
//水题
int gcd(int a,int b)
{
if(a<b)swap(a,b);
if(a%b==0)return b;
else return gcd(a%b,b);
}
int main()
{
int n;
cin>>n;
while(n--)
{
int a,b,c;
cin>>a>>b;
//gcd(a,c)==b;
//b|a;b|c
int x=a/b;
int k;
for(int i=2;;i++)
{
if(gcd(i,x)==1)
{
k=i;
break;
}
}
cout<<k*b<<endl;
}
}
HDU-2588
题意:输入N,M(2<=N<=1000000000, 1<=M<=N), 设1<=X<=N,求使gcd(X,N)>=M的X的个数。
题解:假设N=pd,X=qd.若n与x的最大公约数为d,则能够推出p与q肯定是互质的,因为X<=N所以要求的就是p的欧拉函数值了,那么我们就转化成求满足:N=p*d,并且d>=N的p的欧拉函数值之和了。
int solve(int n)
{
int ans=0;
for(int i=1;i*i<=n;i++)
{
if(n%i!=0)continue;
if(i>=m&&i!=sqrt(n))
{
ans+=Euler(n/i);
}
if(n/i>=m)
{
ans+=Euler(i);
}
}
return ans;
}
直接暴力搜一遍因子就可以,不会超时。
HDU-3071
题意:
给定一个长度为n的序列m次操作,操作的种类一共有三种
查询
L :查询一个区间的所有的数的最小公倍数
G :查询一个区间的所有的数的最大公约数
修改
C :将给定位置的值修改成
考虑到有单点修改和区间查询,考虑线段树或者树状数组的解法。
在这里插入代码片
等我学会了线段树再看…
HDU-4675
题意:
给一个序列 {an}(ai <= M),问有多少种 {bn} ,满足恰好 k 个位置与 {an} 不同,且 gcd of {bn} = d(1<= d <=M)。
看题解,又是要用到莫比乌斯反演的节奏?
很是令人头大…
#include <iostream>
#include <cstdio>
#include <cstring>
#define maxn 300003
#define M 1000000007
typedef long long ll;
using namespace std;
int a[maxn],f[maxn],n,m,k,ai,tep;
ll P[maxn],Q[maxn];
ll power(ll x,ll y)
{
if (x==0) return 0;
if (x==1 || y==0) return 1;
ll tot=1;
while (y)
{
if (y&1) tot=(tot*x)%M;
y>>=1;
x=(x*x)%M;
}
return tot;
}
ll C(ll x,ll y)
{
if (y==0 || x==y) return 1;
ll tot=(P[x]*Q[y])%M;
tot=(tot*Q[x-y])%M;
return tot;
}
int main()
{
ll cur,tot,num;
Q[1]=P[0]=1;
for (int i=1; i<maxn; i++) P[i]=(P[i-1]*i)%M,Q[i]=power(P[i],M-2);
while (scanf("%d%d%d",&n,&m,&k)!=EOF)
{
for (int i=1; i<=m; i++) a[i]=f[i]=0;
for (int i=1; i<=n; i++)
{
scanf("%d",&ai);
a[ai]++;
}
for (int i=m; i>0; i--)
{
cur=0; tot=1; num=m/i;
for (int j=i; j<=m; j+=i) cur+=a[j];
if (n-cur>k)
{
f[i]=0;
continue;
}
tot=power(num,n-cur);
if (n-cur!=k)
{
tep=(C(cur,k+cur-n)*power(num-1,k+cur-n))%M;
tot=(tot*tep)%M;
}
f[i]=tot;
for (int j=i+i; j<=m; j+=i)
{
f[i]-=f[j];
if (f[i]<0) f[i]+=M;
}
}
printf("%d",f[1]);
for (int i=2; i<=m; i++) printf(" %d",f[i]);
printf("\n");
}
return 0;
}
(http://www.cnblogs.com/lochan)
这一段直接用组合数学,还是太菜了啊…
HDU-4676
在这里插入代码片
HDU-2104
题意:n个人围成的一个圈,其中有一个人的盒子里面有一个漂亮的帽子,haha从1号开始找,每隔m-1个人找一次,问他最后是否一定能够找到这个帽子。
题解:一定找到这个帽子的话就每个人都要找一遍,n,m互素.
水题
HDU-4497
题意:告诉了三个数的最大公约数和最小公倍数,要你求出这三个数字有多少种可能(次序不同,排列不同)
显然可以发现,当m%n!=0时,即最小公倍数不是最大公约数的倍数关系时,不存在这三个数。
当m%n==0时,令x=m/n,由正数分解定理可知,x=p1^t1 * p2^t2 * ````* pn^tn,显然,对于每一个pi,三个书中有一个对应指数必须为ti,一个对应指数为0,另外一个对应指数从0到ti任意,组合数为6ti。综上解为6t16t2*…6tn。每一个p,t用分解法贪心,唯一要注意的是,x可能为素数,最后要判断x经过连除之后是否为0,在这WA了三次…
#include<iostream>
using namespace std;
int main()
{
int t;
cin>>t;
while(t--)
{
int n,m;
cin>>n>>m;
if(m%n)
{
cout<<0<<endl;
continue;
}
else
{
int daan=1;
int x=m/n;
if(x==1)
{
cout<<1<<endl;
}
else
{
int xx=x;
for(int i=2;i*i<=xx;i++)
{
int ans=0;
if(x%i==0)
{
while(x%i==0)
{
x/=i;
ans++;
}
daan*=(6*ans);
}
}
if(x!=1)daan*=6;
cout<<daan<<endl;
}
}
}
}
HDU-5019
题意:求两个数第k大的公因子
题解:求两个数最大公约数的第k大公因子
ps.我是真的没想到这么简单…
菜哭辽…
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
typedef long long LL;
LL gcd(LL a,LL b){
return b==0?a:gcd(b,a%b);
}
LL p[10005];
LL cmp(LL a,LL b){
return a>b;
}
int main()
{
int tcase;
scanf("%d",&tcase);
while(tcase--){
LL a,b,k;
scanf("%lld%lld%lld",&a,&b,&k);
LL d = gcd(a,b);
int id = 0;
for(LL i=1;i*i<=d;i++)
{
if(d%i==0){
if(i*i==d) p[id++]=i;
else{
p[id++]=i;
p[id++]=d/i;
}
}
}
if(id<k){
printf("-1\n");
}
else{
sort(p,p+id,cmp);
printf("%lld\n",p[k-1]);
}
}
return 0;
}
先看这几条,明天接着刷。