CF-Round 86-div2-C题
C. Yet Another Counting Problem
这道题是数学 + 模拟 + 前缀和题
题目大意:给出a, b。问询问队列所给的l, r之间有多少数字符合:
((x mod a) mod b) ≠ ((x mod b) mod a);
输出此区间符合要求的答案。
本题思路:暴力肯定行不通的,l和r的数据范围达到了1e18。询问次数达到了500.我们观察式子问的是一个数x模a后再模b的结果和数x模b后再模a的结果不相等的区间内符合要求的个数。
我们想想我们是否可以通过预处理的方式线性时间解决。
我们很容易得到:
((x mod a) mod b) =(((x + ab) mod a)mod b);
这很明显吧。
那么这个式子我们可以得到什么呢?
我们可以发现我们只需要求出[0, ab]之间的所有数据是否满足((x mod a) mod b) ≠ ((x mod b) mod a);这个式子的要求。因为是按照[0, ab]区间来循环的,后面的区间就是新的一轮[0, ab]的循环。
所以我们在输入a, b时就可以先预处理区间[0, ab];记录前缀和pre[]:pre[i]代表0~i的数字中符合上述式子要求的个数。
很明显pre[0] = 0;因为(0 mod a) mod b = (0 mod b) mod a = 0嘛;
之后我们就按照队列的询问得到l, r;
现在我们要求[l, r],按照前缀和的处理形式我们需要获得[0, l - 1]的符合要求的数目和[0, r]的符合要求的数目。之后按照求区间和的方式处理即可。
我们只需要看看到0到l - 1和0到r;[0, l - 1], [0, r]这两个区间包含了多少个[0, ab]区间,cnt记录下来,比如求l得时候:cnt = (l - 1) / (a * b),得到pre[a * b] * cnt,之后我们只需要加上余下的部分即可。比如求(l - 1)的余下部分:就是res = l % (a * b); 加上pre[res]即可。
输出的时候是实时输出的,没有进行保存,如果想输出好看的话可以采用数组保存然后最后一起输出。
代码部分:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 4e4 + 10;
int len;
int pre[N];
void build(int a, int b)
{
len = a * b;
pre[0] = 0;
for (int i = 1; i <= len; i++)
{
pre[i] = pre[i - 1];
if ((i % a) % b != (i % b) % a)
{
pre[i]++;
}
}
}
ll solve(ll x)
{
ll cnt = x / len;
int t = x % len;
return 1ll * pre[len] * cnt + pre[t];
}
ll query(ll l, ll r)
{
return (solve(r) - solve(l - 1));
}
int main()
{
int t;
cin >> t;
while (t--)
{
int a, b, q;
cin >> a >> b >> q;
build(a, b);
ll l, r;
while (q--)
{
cin >> l >> r;
cout << query(l, r) << " ";
}
cout << endl;
}
return 0;
}