1342C. Yet Another Counting Problem(找规律+前缀和)

You are given two integers a and b, and q queries. The i-th query consists of two numbers li and ri, and the answer to it is the number of integers x such that li≤x≤ri, and ((xmoda)modb)≠((xmodb)moda). Calculate the answer for each query.
Recall that ymodz is the remainder of the division of y by z. For example, 5mod3=2, 7mod8=7, 9mod4=1, 9mod9=0.

Input

The first line contains one integer t (1≤t≤100) — the number of test cases. Then the test cases follow.
The first line of each test case contains three integers a, b and q (1≤a,b≤200; 1≤q≤500).
Then q lines follow, each containing two integers li and ri (1≤li≤ri≤1018) for the corresponding query.

Output

For each test case, print q integers — the answers to the queries of this test case in the order they appear.

Example

input
2
4 6 5
1 1
1 3
1 5
1 7
1 9
7 10 2
7 8
100 200
output
0 0 0 2 4
0 91

题目大意:给你a,b两个数和q组询问,每组询问给出区间[l,r],求在[l,r]这个区间里的数有多少个能使得(x%a)%b!=(x%b)%a成立。

这道题。。。。哎!不说了。

题目分析:
通过分析(打表),我们可以发现这样一个规律:
以样例二为例
在1-200区间内不满足(x%7)%10!=(x%10)%7这个条件的数有:1-9,70-79,140-149这九个数。7和10的最小公倍数 70。
由此我们可以发现:不满足条件的数所在的区间为[k * a和b的最小公倍数,k * a和b的最小公倍数+max(a,b)] (k为整数,且k可以等于0).

这样时间复杂度可以优化不少,但还是不够(我当时就只是搞到了这里)。
我们还需要加一个优化:前缀和优化(无敌的前缀和)
用一个数组来存一个周期中,有多少数是满足条件的,并用前缀和处理。这样我们就可以求出1-x(x为任意整数)中有多少数是符合条件的。答案用算出的1一r和1一l-1中符合条件的数相减即可。

1-x中有多少符合条件的数的求法:先算出x覆盖了多少个完整的周期,即:f[n-1]*(x/n)。再加上剩余部分f[x%n]即可。

ac代码如下:

#include <iostream>
#include <cstdio>
#include <cmath>
#include <string>
#include <cstring>
#include <queue>
#include <vector>
#include <algorithm>
#include <iomanip>
#define LL long long
using namespace std;
const int N=1e5+5;
int f[N];
int gcd(int a,int b)     //求最小公倍数
{
    return b?gcd(b,a%b):a;
}
int lcm(int a,int b)     //利用最小公倍数求出最大公约数
{
    return a*b/gcd(a,b);
}
LL cal(LL x,LL n)       //计算1-x中符合条件的数
{
	return f[n-1]*(x/n)+f[x%n];
}
int main()
{
	int t;
	scanf("%d",&t);
	while(t--)
	{
		int a,b,q;
		scanf("%d%d%d",&a,&b,&q);
		int n=lcm(a,b);           //n为周期长度
		for(int i=1;i<n;i++)      //前缀和优化过的记录周期的数组
		{
			f[i]=f[i-1];
			if((i%a)%b!=(i%b)%a) f[i]++;
		}
		while(q--) 
		{
			LL l,r;        //l,r的范围爆int
			scanf("%lld%lld",&l,&r);
			//算出答案并输出即可
			printf("%lld ",cal(r,n)-cal(l-1,n));
		}
	}
    return 0;
}

猜你喜欢

转载自blog.csdn.net/li_wen_zhuo/article/details/105791313