几天没上pongo,昨晚回宿舍前看到出新题了,没时间做,今天上午过来开始做,中间各种杂事,断断续续想了几次,大概在吃午饭前想到了思路,吃饭回来写了下,提交却遇到pongo编译程序崩溃,过了半下午终于可以编译了~^..^~,下面首先是题目内容:
a,b,x,y的范围很大所以不可能去遍历,开始想了几种方法都不理想,下面我还是以a = 8, b = 10, x = 3 , y = 20来讲解我的思路,
先不考虑x,y,只考虑a,b能能覆盖的整数:
n=1 [a*n,b*n] = [8,10] 完全覆盖
n=2 [a*n,b*n] = [16,20] 完全覆盖
n=3 [a*n,b*n] = [24,30] 完全覆盖
n=4 [a*n,b*n] = [32,40] 完全覆盖
n=5 [a*n,b*n] = [40,50] 完全覆盖
n=6 [a*n,b*n] = [48,60] 完全覆盖
....
发现从n=5开始,本次区间与上一次的区间开始有重合,导致后面所有的数均能覆盖,可以看出上例中的连续覆盖的起点是32,它可以这样求得
8n<=10(n-1) ---> n>=5 -->从n=5处开始和上次的区间有重叠,从n=4处开始连续覆盖。
然而此例中连续覆盖起始值为32,比20要大,所以只需计算下连续覆盖起始之前的几个区间能覆盖多少个数字即可
化为一般情况求开始有重合处n:
a*n<=b*(n-1)
n>=b/(b-a)
即n=b/(b-a)对上取整
那么连续覆盖的起始点为z= (n-1)*a
当z<=x时,能覆盖[x,y]区间
当x<z<=y时,能覆盖[z,y],并且需要判断1到n-2倍那些区间能覆盖哪些数,
当z>y时,需要判断1到n-2倍那些区间能覆盖哪些,
下面是代码:
#include<iostream>
#include<stdio.h>
#include<string>
using namespace std;
class Test {
public:
static int howmany (int a,int b,int x,int y){
int res=0;
int n=b/(b-a);
if(0!=b%(b-a))
++n;
long long z=a*(n-1);//
//std::cout<<z<<std::endl;
if(z<=x){
res=y-x+1;
}else if(z<=y){
res+=y-z+1;
y=z-1;
for(int i=n-2;i>=1;--i){
int tmpUp=i*b;
if(tmpUp<x)
break;
int tmpDown=i*a;
if(tmpDown<x){
res+=tmpUp-x+1;
}else{
res+=tmpUp-tmpDown+1;
}//else
}//for
}else{//z>y
for(int i=n-2;i>=1;--i){
int tmpUp=i*b;
if(tmpUp<x)
break;
int tmpDown=i*a;
if(tmpDown>y)
continue;
tmpUp=tmpUp>y?y:tmpUp;
tmpDown=tmpDown<x?x:tmpDown;
res+=tmpUp-tmpDown+1;
}//for
}//else
return res;
}
};
//start 提示:自动阅卷起始唯一标识,请勿删除或增加。
int main()
{
cout<<Test::howmany(8,10,3,20)<<endl;
}
//end //提示:自动阅卷结束唯一标识,请勿删除或增加。
上面代码通过了pongo,但是代码存在溢出的风险,并且可以对循环检测1至n-2倍处做些优化,如下:
#include<iostream>
#include<stdio.h>
#include<string>
using namespace std;
class Test {
public:
static int howmany (int a,int b,int x,int y){
int res=0;
long long tmpA=a,tmpB=b;//乘法中,位数向高位数转换,int-->double
int n=b/(b-a);
if(0!=b%(b-a))
++n;
long long z=tmpA*(n-1);//
if(z<=x){
res=y-x+1;
}else if(z<=y){
res+=y-z+1;
// y=z-1;
for(int i=n-2;i>=1;--i){
long long tmpUp=i*tmpB;
if(tmpUp<x)
break;
long long tmpDown=i*tmpA;
if(tmpDown<x){
res+=tmpUp-x+1;
}else{
res+=tmpUp-tmpDown+1;
}//else
}//for
}else{//z>y
int k=y/tmpA;
if(k>n-2)
k=n-2;
for(int i=k;i>=1;--i){
long long tmpUp=i*tmpB;
if(tmpUp<x)
break;
long long tmpDown=i*tmpA;
if(tmpDown>y)
continue;
tmpUp=tmpUp>y?y:tmpUp;
tmpDown=tmpDown<x?x:tmpDown;
res+=tmpUp-tmpDown+1;
}//for
}//else
return res;
}
};
//start 提示:自动阅卷起始唯一标识,请勿删除或增加。
int main()
{
cout<<Test::howmany(99999999,100000000,100000001,1000000000)<<endl;
}
//end //提示:自动阅卷结束唯一标识,请勿删除或增加。
此代码也通过了pongo