Luogu P10785 / NOI2024 Day 1 T1 Solution
题面
思路
首先,考虑一个满足要求的区间有什么性质。
等价的定义非常复杂,这里翻译一下。
一个区间是符合要求的,当且仅当我们可以将区间内 \(a\) 集合中的每个数替换成一个 \([1,m]\) 区间内的整数,且满足操作前相等的数在操作后仍然相等,操作前不相等的数在操作后仍然不相等,使得最后的集合与 \(b\) 在区间内的集合完全相同。
换句话说,如果存在一个集合 \(S\) 和一个整数 \(x\),使得 \(S\) 中的任意一个元素 \(i\) 满足 \(x\) 在集合 \(a_i\) 中出现过,且 \(S\) 为 \(x\) 在 \(a\) 中出现的所有位置,那么必然存在另一个整数 \(y\),使得 \(S\) 中的任意一个元素 \(i\) 满足 \(y\) 在集合 \(b_i\) 中出现过,且 \(S\) 为 \(y\) 在 \(b\) 中出现的所有位置。\(x\) 可以等于 \(y\)。
也就是说,如果对于出现在区间内 \(a\) 集合中的任意一个整数 \(x\) 都有一个整数 \(y\) 使得它们在 \(a\) 和 \(b\) 中出现的集合位置一样,那么区间满足条件。
解法
考虑哈希+双指针。
为 \(1\) 到 \(n\) 的每个整数 \(i\) 赋一个随机值 \(h_i\)。
定义一个整数 \(x\) 出现在一个集合组 \(a\) 中的所有位置 \(i\) 的 \(h_i\) 之和为 \(x\) 对于集合组 \(a\) 的位值 \(v_{a,x}\)。
如果用数学语言来写的话,
同时,定义一个集合组 \(a\) 的位值集合 \(s_a\) 为所有 \([1,m]\) 范围内整数 \(i\) 的 \(v_{a,i}\) 组成的可重集。
如果用数学语言来写的话,
不难发现,按照上述方式计算区间内的 \(s_a\) 和 \(s_b\) 后,若 \(s_a=s_b\),则区间内 \(a\) 中出现的每个数都可以在 \(b\) 中找到满足条件的映射,则区间满足条件,否则不满足。
同时,注意到区间越大 \(s_a=s_b\) 的概率越小,即 \(s_a=s_b\) 具有单调性,考虑通过双指针贪心地对于每一个区间右端点 \(r\) 找到最小的 \(l\) 使得 \([l,r]\) 满足题目条件,设定此时的 \(l\) 为 \(q_r\)。若在后续询问中,对于区间 \([l,r]\) 满足 \(l\ge q_r\),则该区间一定满足 \(s_a=s_b\)。
故题目解决。
视使用 map
或 unordered_map
作为哈希值储存容器时间复杂度分别为 \(O(n\log n)\) 和 \(O(n)\)。
代码
#include <iostream>
#include <map>
#include <random>
using namespace std;
const int N = 2e5 + 10, M = 6e5 + 10;
int n, m, q, x, y, a[N][3], b[N][3], cur, req[N];
using ull = unsigned long long;
map<ull, int> mp;
random_device rd;
mt19937 mt(rd());
ull sg[N], pa[M], pb[M];
void upd(ull x, int v)
{
cur += !mp[x];
mp[x] += v;
cur -= !mp[x];
}
void acs(int x, int v)
{
for (int i = 0; i < 3; i++)
{
upd(pa[a[x][i]], -1);
pa[a[x][i]] += v * sg[x];
upd(pa[a[x][i]], 1);
upd(pb[b[x][i]], 1);
pb[b[x][i]] += v * sg[x];
upd(pb[b[x][i]], -1);
}
}
int main()
{
scanf("%d%d%d", &n, &m, &q);
for (int i = 1; i <= n; i++)
{
for (int j = 0; j < 3; j++)
{
scanf("%d", a[i] + j);
}
}
for (int i = 1; i <= n; i++)
{
for (int j = 0; j < 3; j++)
{
scanf("%d", b[i] + j);
}
}
for (int i = 1; i <= n; i++)
{
sg[i] = mt();
}
for (int i = 1, j = 1; i <= n; i++)
{
acs(i, 1);
while (cur)
acs(j, -1), j++;
req[i] = j;
}
for (int i = 1; i <= q; i++)
{
scanf("%d%d", &x, &y);
puts(req[y] <= x ? "Yes" : "No");
}
}