Luogu P10785 / NOI2024 Day 1 T1 Solution

题面

here

民间数据

思路

首先,考虑一个满足要求的区间有什么性质。

等价的定义非常复杂,这里翻译一下。

一个区间是符合要求的,当且仅当我们可以将区间内 \(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}\)

如果用数学语言来写的话,

\[v_{a,x}=\sum_{i=1}^n h_i[x\in a_i] \]

同时,定义一个集合组 \(a\)位值集合 \(s_a\) 为所有 \([1,m]\) 范围内整数 \(i\)\(v_{a,i}\) 组成的可重集。

如果用数学语言来写的话,

\[s_a=\set{v_{a,i}|1\le i\le m} \]

不难发现,按照上述方式计算区间内的 \(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\)

故题目解决。

视使用 mapunordered_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");
   }
}
posted @ 2024-07-25 18:17  丝羽绫华  阅读(33)  评论(0编辑  收藏  举报