【小Y的数学作业】(Homework.pas/c/cpp Time:1s Memory:256M)
【问题描述】
小Y是个很好学的孩子。(附注:Y=yang)
最近老师总是布置给他(或者她)一些数学作业题,每道作业题就是求一个数X与数字A的最大公约数和X与数字B的最小公倍数。这天晚上11:00,大黄到小Y家去“玩”,不小心弄翻了小Y的咖啡,结果N道题的数字X都看不见了,但是数字A、数字B与小Y算出来的答案都还在。小Y很急,想问你对于每一道数学作业题,到底有多少种X满足已经算出的答案。
【输入】
输入文件名为homework.in。
第一行一个整数N,代表数学老师布置的题目数量。
接下来N行,每行4个正整数A,A1,B,B1,代表Gcd(x,A)=A1,Lcm(x,B)=B1。
【输出】
输出文件名为homework.out
对于每个数学题,若存在这样的X,即输出合法的X的个数,否则输出0。
【输入输出样例】
Homework.in
2
41 1 96 288
95 1 37 1776
Homework.out
6
2
【数据范围】
对于50%的数据,保证有1≤A,A1,B,B1≤10000且n≤100。
对于100%的数据,保证有1≤A,A1,B,B1≤2,000,000,000且n≤2000。
【题解】
原题:NOIP2009第二题 Hankson 的趣味题 (传送门)
首先,如果B1无法整除A1,那么显然是无解的。
然后,对于某一个质因数P来说,假设A包含p的最高次幂为a,A1为b,B为c,B1为d,然后X包含p的最高次幂为x。
那么x必须满足如下条件:
Min(x,a)=b;Max(c,x)=d;
这个是最大公约数和最小公倍数的基本性质。
通过这两个条件,我们可以算出x的取值范围。因为不同质因数之间是互不影响的,所以就直接乘起来即可。
对于
109
数分解质因数可以先把1到100000之间的质数筛出来,然后枚举1到100000以内的质数,如果这样之后还有剩余的部分那么就是最后那个剩下的质数了。
【代码】
#include <cstdio>
int a0,a1,b0,b1,t;
inline int gcd(int x,int y){
int i,j;
if(x==0) return y;
if(y==0) return x;
for(i=0;(x&1)==0;i++) x>>=1;
for(j=0;(y&1)==0;j++) y>>=1;
if(j<i) i=j;
while(1) {
if(x<y) x^=y,y^=x,x^=y;
if((x-=y)==0) return y<<i;
while((x&1)==0) x>>=1;
}
}
bool judge(int x) {
return !(x%a1)&&1==gcd(x/a1,a0/a1)&&1==gcd(b1/b0,b1/x);
}
int main(){
scanf("%d",&t);
while(t--) {
scanf("%d%d%d%d",&a0,&a1,&b0,&b1);
int p=a0/a1;
int ans=0;
for(int k=1;k*k<=b1;k++) {
if(!(b1%k)) {
ans+=judge(k);
if(k*k!=b1)
ans+=judge(b1/k);
}
}
printf("%d\n",ans);
}
return 0;
}
【小Y的智力游戏】(Game.pas/c/cpp Time:1s Memory:256M)
【问题描述】
小Y最近迷上了一款智力游戏。(当然小Y还是很好学的)
这款智力游戏就是让你从1到N这N个数中,选取若干个不相等的正整数,使得它们的乘积为一个完全平方数,同时,这个乘积也将成为你的得分。
他(或她,以后这个括号就省略了)想知道最大得分对1000000007的模值。
【输入】
输入文件名game.in
一行一个正整数N,代表数字的个数。
【输出】
输出文件名game.out
一行一个正整数,代表最大得分对1000000007的模值。
【输入输出样例】
Game.in Game.out
3 1
【数据范围】
对于30%的数据,保证有n≤50。
对于60%的数据,保证有n≤1000。
对于70%的数据,保证有n≤10000。
对于80%的数据,保证有n≤100000。
对于90%的数据,保证有n≤1000000。
对于100%的数据,保证有n≤3000000。
【题解】
将 n! 唯一分解,然后将指数为奇数的质因子的指数减去一,得到的唯一分解式就是答案
【代码】
#include <cstdio>
#include <iostream>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long LL;
const int mod = 1000000007;
const int size = 3000010;
LL n,ans=1,tmp;
LL prime[size],cl[size],fir[size];
bool no_prime[size];
void fenjie(int x) {
if(x==1)
return ;
LL ret = fir[x];
while(x%prime[ret]==0) {
x/=prime[ret];
cl[ret]++;
}
fenjie(x);
}
LL powermod(LL x,LL y,LL p) {
if(y==0)
return 1;
if(y==1)
return x%p;
LL ret = powermod(x,y/2,p);
ret = ret*ret%p;
if(y&1)
ret = ret*(x%p)%p;
return ret;
}
int main() {
freopen("game.in","r",stdin);
freopen("game.out","w",stdout);
scanf("%lld",&n);
for(LL i=2;i<=n;i++) {
if(!no_prime[i]) {
prime[++tmp]=i;
fir[i]=tmp;
}
for(LL j=1;prime[j]*i<=n;j++) {
fir[prime[j]*i]=j;
no_prime[prime[j]*i]=true;
if(i%prime[j]==0)
break;
}
}
for(LL i=2;i<=n;i++)
fenjie(i);
for(LL i=1;i<=tmp;i++) {
if(cl[i]&1)
ans = ans*powermod(prime[i],cl[i]-1,mod)%mod;
else
ans = ans*powermod(prime[i],cl[i],mod)%mod;
}
printf("%lld\n",ans);
return 0;
}
【小Y的绝对战争】(War.pas/c/cpp Time:1s Memory:256M)
【问题描述】
小Y与大黄爆发了一场绝对要胜利的战争。(原因是大黄生活作风问题)
小Y现在拥有N个城池,每个城池都有一个强大值Wi。现在没有一条通路连接任意两个城池(通路是双向的)。现在小Y想顺次修建若干条通路,使得每修建一条通路都连接了两个原本不连通的城池(联通的定义是两个城池之间存在一条由通路构成的路径)。每修建一条通路IJ,那么安全值就会增加Gcd(Wi,Wj)。为了确保这场战争的胜利,小Y想知道,可能的最大安全值是多少(囧)。
【输入】
输入文件名为war.in。
第一行一个正整数N,代表城池的个数。
接下来N行,每行一个正整数,代表这个城市的强大值。
【输出】
输出文件名为war.out。
一行一个整数,代表最大的安全值。
【输入输出样例】
War.in
4
1
2
3
4
War.out
4
【数据范围】
对于30%的数据,保证有n≤1000。
对于100%的数据,保证有n≤1000000, 任意W小于等于1000000。
【题解】
这是显然是求一颗最大生成树。首先我们可以想到一个Kruskal的解法。也就是N^2LogN的。但是,很明显因为权值在1到1000000所以不同权值也最多只有1000000,也就是说,可以直接从大到小枚举权值,将点合并。对于一个权值I,可以取的所有点就是I的权值的倍数。这样,时间复杂度就降低为 O(NlnN) 。
考试时我是直接 O(n2) 出解
【代码】
暂无