CF-Round 86-div2-C题

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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

娃娃酱斯密酱

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值