【Luogu】P9809 [SHOI2006] 作业 Homework (根号算法)
题目:
思路:
我们不难发现本题和 mod 有关,想这种难以维护所有情况的时候,我们可以往根号算法上想
本题中值域为 V = 3e5,不难发现一个特点我们可以以 B = sqrt(V) 为边界分两种方法讨论
①.如果 Y <= B
此时我们可以暴力枚举所有情况的答案,处理时间复杂度最多为 O(N*B),查询只需要 O(1)
②.如果 Y > B
此时如何处理呢?注意到我们只需要余数最小,那么我们可以往商的方向上想,如果直到商,那么余数也就出来了,所以我们可以考虑枚举商然后去二分第一个大于等于商的数,此时这个数的余数一定是所有相同商中的最小余数,时间复杂度为 O(N*B*logN),注意枚举商是 sqrt(V) 的复杂度
具体实现看代码
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define yes cout << "YES\n"
#define no cout << "NO\n"
mt19937 rnd(chrono::steady_clock::now().time_since_epoch().count());const int B = 550;
// mod i 的最小结果
int ans[B];void solve()
{int n;cin >> n;set<int> has;for (int i = 0; i < n; i++){char c;cin >> c;if (c == 'A'){int x;cin >> x;for (int i = 1; i <= B; i++)ans[i] = min(ans[i], x % i);has.insert(x);}else{int y;cin >> y;if (y >= B){int now = y;//枚举商的结果,即枚举 ky,那么最接近的肯定是余数最小的,二分寻找即可for (int i = 0; i <= 300000; i+=y){auto it = has.lower_bound(i);if(it == has.end()) break;now = min(now,*it - i);}cout << now << endl;}elsecout << ans[y] << endl;}}
}signed main()
{memset(ans, 0x3f, sizeof ans);ios::sync_with_stdio(false);cin.tie(nullptr);int t = 1;while (t--){solve();}return 0;
}