题目:
Description |
Kim刚刚学会C语言中的取模运算(mod)。他想要研究一下一个数字A模上一系列数后的结果是多少。帮他写个程序验证一下。 |
Input |
第一行一个整数T代表数据组数。 接下来T组数据,第一行一个整数n,接下来n个数字ai 接下来一行一个整数m,接下来m个数字bi。 |
Output |
对于每个bi,输出bi%a1%a2%...%an 。 |
Sample Input |
1 4 10 9 5 7 5 14 8 27 11 25 |
Sample Output |
4 3 2 1 0 |
Hint |
在C语言中,A mod B 是 a%b 样例解释: 14%10%9%5%7=4 8%10%9%5%7=3 ... 数据范围: 1<=n<=100000 1<=m<=100000 1<=ai<=1000000000 0<=bi<=1000000000 |
分析:如果很多个数字对一个数字进行取模,第二个取模会把第一个取得模改变的数字,一定会比第一次取模得到的结果小,因此有效的取模的数字首先应该是递减的。这样,我们就可以在输入ai的时候,把无效的数字删除,留下一串递减的数组。
然后,最重要的一步,在数组中找到上次取模得到的结果比它小于或者等于的第一个数,然后用这个数字对上次得到的结果进行取模操作。
如果对每次向后面遍历一遍呢,遍历的时间复杂度是O(n),一共会输入m个数字,所以总的时间复杂度是O(mn),显然,在这种情况下无法满足时间要求。因此,为了能提高效率,只能在查找时进行优化,我们可以采用二分法,二分地来查找每次要找到的数字的位置,这样,查找的时间复杂度为O(log(n)),总的时间复杂度是O(m log(n)),可以在最坏的情况下在时间要求之内得到结果。
下面是标准程序(本人亲测)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<vector>
#include<stack>
#include<set>
#include<map>
#include<queue>
#include<algorithm>
using namespace std;
long long num[100005];
int fin(int low,int high,int val){
if(high-low<=1)
return low;
int mid=(low+high)/2;
if(num[mid]<=val)
fin(low,mid,val);
else
fin(mid,high,val);
}
int main(){
int t;
int n;
int m;
int i,j,k;
int b;
long long last;
int coun;
long long temp;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
scanf("%lld",&last);
num[0]=last;
coun=1;
for(i=1;i<n;i++){
scanf("%lld",&temp);
if(temp<last){
last=temp;
num[coun]=last;
coun++;
}
}
scanf("%d",&m);
for(i=0;i<m;i++){
scanf("%d",&b);
int x=0;
while(x<coun&&b!=0){
b%=num[x];
x=fin(x+1,coun-1,b);
}
printf("%d\n",b);
}
}
return 0;
}