2305: Hankson 的趣味题(NOIP 2009 TGT2)
题目描述
Hanks 博士是 BT(Bio-Tech,生物技术) 领域的知名专家,他的儿子名叫 Hankson。现在,刚刚放学回家的 Hankson 正在思考一个有趣的问题。 今天在课堂上,老师讲解了如何求两个正整数 c1�1 和 c2�2 的最大公约数和最小公倍数。现在 Hankson 认为自己已经熟练地掌握了这些知识,他开始思考一个“求公约数”和“求公倍数”之类问题的“逆问题”,这个问题是这样的:已知正整数a0,a1,b0,b1�0,�1,�0,�1,设某未知正整数 x� 满足:
1. x� 和 a0�0 的最大公约数是 a1�1;
2. x� 和 b0�0 的最小公倍数是 b1�1。
Hankson 的“逆问题”就是求出满足条件的正整数 x�。但稍加思索之后,他发现这样的 x� 并不唯一,甚至可能不存在。因此他转而开始考虑如何求解满足条件的 x� 的个数。请你帮助他编程求解这个问题。
输入
第一行为一个正整数 n�,表示有 n� 组输入数据。接下来的n� 行每行一组输入数据,为四个正整数 a0,a1,b0,b1�0,�1,�0,�1,每两个整数之间用一个空格隔开。输入数据保证 a0�0 能被 a1�1 整除,b1�1 能被 b0�0 整除。
输出
共 n� 行。每组输入数据的输出结果占一行,为一个整数。 对于每组数据:若不存在这样的 x�,请输出 00,若存在这样的 x�,请输出满足条件的 x� 的个数;
样例输入 复制
2 41 1 96 288 95 1 37 1776
样例输出 复制
6 2
提示
【样例解释】
第一组输入数据,x� 可以是 9,18,36,72,144,2889,18,36,72,144,288,共有 66 个。
第二组输入数据,x� 可以是 48,177648,1776,共有 22 个。
【数据范围】
对于 50%50% 的数据,保证有 1≤a0,a1,b0,b1≤100001≤�0,�1,�0,�1≤10000 且 n≤100�≤100。
对于 100%100% 的数据,保证有 1≤a0,a1,b0,b1≤2×1091≤�0,�1,�0,�1≤2×109 且 n≤2000�≤2000。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<bitset>
#include<set>
using namespace std;
int gcd(int a,int b){return b==0 ? a:gcd(b,a%b);}//辗转相除法
int main()
{
int T;
scanf("%d",&T);
while(T--){
int a0,a1,b0,b1;
scanf("%d%d%d%d",&a0,&a1,&b0,&b1);
int p=b1/b0,q=a0/a1,ans=0;
for(int x=1;x*x<=b1;x++){
if(b1%x!=0) continue;//枚举到的必须是b1的因子
if(x%a1==0&&gcd(q,x/a1)==1&&gcd(p,b1/x)==1) ans++;
int y=b1/x;//找到第二个因子,借此来简化计算
if(x==y) continue;//找重了排除掉
if(y%a1==0&&gcd(q,y/a1)==1&&gcd(p,b1/y)==1) ans++;
}
cout<<ans<<endl;
}
return 0;
}
2309: 乌龟棋(NOIP 2010 TGT2)
题目描述
乌龟棋的棋盘是一行N�个格子,每个格子上一个分数(非负整数)。棋盘第1格是唯一的起点,第N�格是终点,游戏要求玩家控制一个乌龟棋子从起点出发走到终点。 乌龟棋中M�张爬行卡片,分成4种不同的类型(M�张卡片中不一定包含所有44种类型的卡片,见样例),每种类型的卡片上分别标有1,2,3,41,2,3,4四个数字之一,表示使用这种卡片后,乌龟棋子将向前爬行相应的格子数。游戏中,玩家每次需要从所有的爬行卡片中选择一张之前没有使用过的爬行卡片,控制乌龟棋子前进相应的格子数,每张卡片只能使用一次。 游戏中,乌龟棋子自动获得起点格子的分数,并且在后续的爬行中每到达一个格子,就得到该格子相应的分数。玩家最终游戏得分就是乌龟棋子从起点到终点过程中到过的所有格子的分数总和。 很明显,用不同的爬行卡片使用顺序会使得最终游戏的得分不同,小明想要找到一种卡片使用顺序使得最终游戏得分最多。 现在,告诉你棋盘上每个格子的分数和所有的爬行卡片,你能告诉小明,他最多能得到多少分吗?
输入
每行中两个数之间用一个空格隔开。 第11行22个正整数N,M�,�,分别表示棋盘格子数和爬行卡片数。 第22行N�个非负整数,a1,a2,…,aN�1,�2,…,��,其中ai��表示棋盘第i�个格子上的分数。 第33行M�个整数,b1,b2,…,bM�1,�2,…,��,表示M张爬行卡片上的数字。 输入数据保证到达终点时刚好用光M�张爬行卡片。
输出
11个整数,表示小明最多能得到的分数。
样例输入 复制
9 5 6 10 14 2 8 8 18 5 17 1 3 1 2 1
样例输出 复制
73
提示
每个测试点1s1�
小明使用爬行卡片顺序为1,1,3,1,21,1,3,1,2,得到的分数为6+10+14+8+18+17=736+10+14+8+18+17=73。注意,由于起点是11,所以自动获得第11格的分数66。
对于30%30%的数据有1≤N≤30,1≤M≤121≤�≤30,1≤�≤12。
对于50%50%的数据有1≤N≤120,1≤M≤501≤�≤120,1≤�≤50,且44种爬行卡片,每种卡片的张数不会超过2020。
对于100%100%的数据有1≤N≤350,1≤M≤1201≤�≤350,1≤�≤120,且44种爬行卡片,每种卡片的张数不会超过4040;
0≤ai≤100,1≤i≤N,1≤bi≤4,1≤i≤M0≤��≤100,1≤�≤�,1≤��≤4,1≤�≤�。
#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#define MAXN 10010
#define in(a) a=read()
#define REP(i,k,n) for(int i=k;i<=n;i++)
using namespace std;
inline int read(){
int x=0,f=1;
char ch=getchar();
for(;!isdigit(ch);ch=getchar())
if(ch=='-')
f=-1;
for(;isdigit(ch);ch=getchar())
x=x*10+ch-'0';
return x*f;
}
int a,b,c,d;
int v[500],n,m;
int dp[50][50][50][50];
int main(){
in(n),in(m);
REP(i,1,n) in(v[i]);
int x;
REP(i,1,m){
in(x);
if(x==1) a++;
if(x==2) b++;
if(x==3) c++;
if(x==4) d++;
}
dp[0][0][0][0]=v[1];
REP(i,0,a)
REP(j,0,b)
REP(k,0,c)
REP(l,0,d){
int r=1+i+j*2+k*3+l*4;
if(i!=0) dp[i][j][k][l]=max(dp[i][j][k][l],dp[i-1][j][k][l]+v[r]);
if(j!=0) dp[i][j][k][l]=max(dp[i][j][k][l],dp[i][j-1][k][l]+v[r]);
if(k!=0) dp[i][j][k][l]=max(dp[i][j][k][l],dp[i][j][k-1][l]+v[r]);
if(l!=0) dp[i][j][k][l]=max(dp[i][j][k][l],dp[i][j][k][l-1]+v[r]);
}
cout<<dp[a][b][c][d];
return 0;
}
2313: 选择客栈(NOIP 2011 TGD1T2)
题目描述
丽江河边有 n� 家很有特色的客栈,客栈按照其位置顺序从 11 到 n� 编号。每家客栈都按照某一种色调进行装饰(总共 k� 种,用整数 0∼k−10∼�−1 表示),且每家客栈都设有一家咖啡店,每家咖啡店均有各自的最低消费。 两位游客一起去丽江旅游,他们喜欢相同的色调,又想尝试两个不同的客栈,因此决定分别住在色调相同的两家客栈中。晚上,他们打算选择一家咖啡店喝咖啡,要求咖啡店位于两人住的两家客栈之间(包括他们住的客栈),且咖啡店的最低消费不超过 p� 。 他们想知道总共有多少种选择住宿的方案,保证晚上可以找到一家最低消费不超过 p� 元的咖啡店小聚。
输入
共 n+1�+1 行。 第一行三个整数 n,k,p�,�,�,每两个整数之间用一个空格隔开,分别表示客栈的个数,色调的数目和能接受的最低消费的最高值; 接下来的 n� 行,第 i+1�+1 行两个整数,之间用一个空格隔开,分别表示 i� 号客栈的装饰色调 ai�� 和 i� 号客栈的咖啡店的最低消费 bi��。
输出
一个整数,表示可选的住宿方案的总数。
样例输入 复制
5 2 3 0 5 1 3 0 2 1 4 1 5
样例输出 复制
3
提示
样例解释
2 人要住同样色调的客栈,所有可选的住宿方案包括:住客栈①③,②④,②⑤,④⑤,但是若选择住 4,54,5号客栈的话,4,54,5 号客栈之间的咖啡店的最低消费是 44 ,而两人能承受的最低消费是 33 元,所以不满足要求。因此只有前 33 种方案可选。
数据范围
对于 30%30% 的数据,有 n≤100�≤100 ;
对于 50%50% 的数据,有 n≤1000�≤1000;
对于 100%100% 的数据,有 2≤n≤2×1052≤�≤2×105,1≤k≤501≤�≤50,0≤p≤1000≤�≤100,0≤bi≤1000≤��≤100。
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
int n,k,p,m,ans;
int a[200010],b[200010],c[200010];
int main()
{
scanf("%d%d%d",&n,&k,&p);
for (int i = 1; i <= n; i++)
{
int k, q;
scanf("%d%d",&k,&q);
if (q <= p) //找最近的消费≤p的咖啡店
m = i;
if (m >= a[k])
c[k] = b[k]; //更新位置,当前的客栈可以和上一次色调为k的客栈组合成一种方案
a[k] = i;
ans += c[k];
b[k]++;
}
printf("%d",ans);
return 0;
}
2316: 聪明的质监员(NOIP 2011 TGD2T2)
题目描述
`小T` 是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有 n� 个矿石,从 11 到 n� 逐一编号,每个矿石都有自己的重量 wi�� 以及价值 vi�� 。检验矿产的流程是:
1 、给定m� 个区间 [li,ri][��,��];
2 、选出一个参数 W�;
3 、对于一个区间 [li,ri][��,��],计算矿石在这个区间上的检验值 yi��:
yi=∑j=liri[wj≥W]×∑j=liri[wj≥W]vj��=∑�=����[��≥�]×∑�=����[��≥�]��
其中 j� 为矿石编号。
这批矿产的检验结果 y� 为各个区间的检验值之和。即:∑i=1myi∑�=1���
若这批矿产的检验结果与所给标准值 s� 相差太多,就需要再去检验另一批矿产。`小T` 不想费时间去检验另一批矿产,所以他想通过调整参数 W� 的值,让检验结果尽可能的靠近标准值 s�,即使得 |s−y||�−�| 最小。请你帮忙求出这个最小值。
输入
第一行包含三个整数 n,m,s�,�,�,分别表示矿石的个数、区间的个数和标准值。 接下来的 n� 行,每行两个整数,中间用空格隔开,第 i+1�+1 行表示 i� 号矿石的重量 wi�� 和价值 vi��。 接下来的 m� 行,表示区间,每行两个整数,中间用空格隔开,第 i+n+1�+�+1 行表示区间 [li,ri][��,��] 的两个端点 li�� 和 ri��。注意:不同区间可能重合或相互重叠。
输出
一个整数,表示所求的最小值。
样例输入 复制
5 3 15 1 5 2 5 3 5 4 5 5 5 1 5 2 4 3 3
样例输出 复制
10
提示
【输入输出样例说明】
当 W� 选 44 的时候,三个区间上检验值分别为 20,5,020,5,0 ,这批矿产的检验结果为 2525,此时与标准值 S� 相差最小为 1010。
【数据范围】
对于 10%10% 的数据,有 1≤n,m≤101≤�,�≤10;
对于 30%30%的数据,有 1≤n,m≤5001≤�,�≤500 ;
对于 50%50% 的数据,有 1≤n,m≤5,0001≤�,�≤5,000;
对于 70%70% 的数据,有 1≤n,m≤10,0001≤�,�≤10,000 ;
对于 100%100% 的数据,有 1≤n,m≤200,0001≤�,�≤200,000,0<wi,vi≤1060<��,��≤106,0<s≤10120<�≤1012,1≤li≤ri≤n1≤��≤��≤� 。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<queue>
using namespace std;
const int maxn=2e5+5;
typedef long long ll;
int n,m;
ll S;
ll w[maxn],v[maxn];
int wcnt[maxn];
ll vsum[maxn];
int ls[maxn],rs[maxn];
ll ans=1234567890123456ll;
bool check(ll mid)
{
ll sum=0;
for(int i=1;i<=n;i++)
{
wcnt[i]=wcnt[i-1];
vsum[i]=vsum[i-1];
if(w[i]>=mid) wcnt[i]++,vsum[i]+=v[i];
}
for(int i=1;i<=m;i++)
{
int lz=ls[i],rz=rs[i];
sum+=(wcnt[rz]-wcnt[lz-1])*(vsum[rz]-vsum[lz-1]);
}
ans=min(ans,abs(S-sum));
if(sum<S) return 1;
return 0;
}
int main()
{
scanf("%d%d%lld",&n,&m,&S);
ll maxnum=0,minnum=1000007;
for(int i=1;i<=n;i++)
{
scanf("%lld%lld",&w[i],&v[i]);
maxnum=max(maxnum,w[i]);
minnum=min(minnum,w[i]);
}
for(int i=1;i<=m;i++)
{
scanf("%d%d",&ls[i],&rs[i]);
}
ll l=minnum,r=maxnum+2,mid;
while(r-l>1)
{
mid=l+r>>1;
if(check(mid)) r=mid;
else l=mid;
}
printf("%lld\n",ans);
return 0;
}