G - Mediator
G - Mediator
Problem Statement
Beware the special input format and the smaller memory limit than usual.
There is an undirected graph with vertices , initially without edges.
You need to process the following queries on this graph:
1 u v
Type : Add an edge between vertices and .
Before adding the edge, and belong to different connected components (i.e., the graph always remains a forest).
2 u v
Type : If there is a vertex adjacent to both and , report its vertex number; otherwise, report .
Given that the graph always remains a forest, the answer to this query is uniquely determined.
The queries are given in an encrypted form.
The original query is defined by three integers , and the encrypted query is given as three integers .
Let be the answer to the -th type- query. Define for .
Restore from the given as follows:
- Let be the number of type- queries given before this query (not counting the query itself). Then, use the following:
Constraints
- All input values are integers.
Input
The input is given from Standard Input in the following format:
Output
Let be the number of type- queries. Print lines.
The -th line should contain the answer to the -th type- query.
Sample Input 1
6 12
143209915 123840720 97293110
89822758 207184717 689046893
67757230 270920641 26993265
952858464 221010240 871605818
730183361 147726243 445163345
963432357 295317852 195433903
120904472 106195318 615645575
817920568 27584394 770578609
38727673 250957656 506822697
139174867 566158852 412971999
205467538 606353836 855642999
159292205 319166257 51234344
Sample Output 1
0
2
0
2
6
0
1
After decrypting all queries, the input is as follows:
6 12
2 1 3
1 2 6
1 2 4
1 1 3
2 4 6
2 1 4
1 5 6
1 1 2
2 1 4
2 2 5
2 3 4
2 2 3
This input has a -vertex graph and queries.
- The first query is
2 1 3
.- No vertex is adjacent to both vertex and vertex , so report .
- The second query is
1 2 6
.- Add an edge between vertices and .
- The third query is
1 2 4
.- Add an edge between vertices and .
- The fourth query is
1 1 3
.- Add an edge between vertices and .
- The fifth query is
2 4 6
.- The vertex adjacent to both vertices and is vertex .
- The sixth query is
2 1 4
.- No vertex is adjacent to both vertices and , so report .
- The seventh query is
1 5 6
.- Add an edge between vertices and .
- The eighth query is
1 1 2
.- Add an edge between vertices and .
- The ninth query is
2 1 4
.- The vertex adjacent to both vertices and is vertex .
- The tenth query is
2 2 5
.- The vertex adjacent to both vertices and is vertex .
- The eleventh query is
2 3 4
.- No vertex is adjacent to both vertices and , so report .
- The twelfth query is
2 2 3
.- The vertex adjacent to both vertices and is vertex .
Sample Input 2
2 1
377373366 41280240 33617925
Sample Output 2
The output may be empty.
解题思路
容易想到的暴力思路是,给每个节点开一个 std::set
存储邻点。对于询问 ,枚举 的每个邻点 并判断 是否为 的邻点。一次询问的时间复杂度为 。
个询问肯定会超时。我们本质是想知道 与 的邻点是否有交集,为此可以给每个节点开一个 std::bitset
标记其邻点(如果与节点 相邻则第 位置为 )。对于询问 则只需将对应的两个 std::bitset
按位与然后找到为 的位置(或报告没有)。一次询问的时间复杂度为 ,在 的时限内是可以通过的,但空间复杂度是 ,显然会爆掉。
当我们想到两种暴力做法时可以尝试根号分治了,由于是图论的问题,一般根据节点的轻重来分类,参考交友问题。设置一个阈值 ,当节点的度数超过 时(也就是重点),就给这个节点开一个 std::bitset
。容易知道重点的数量大致是 级别的,因此 std::bitset
的空间复杂度就是 。
由于是强制在线,所以在加边的过程中需要动态维护节点的度数,当加完变后节点的度数超过 ,就需要为该节点开一个 std::bitset
,然后枚举 std::set
中的节点进行标记。在所有的询问中,总计算量是 。
对于询问 ,不失一般性假设 比 的度数小(否则可以交换 和 而不影响结果)。如果 的度数不超过 ,则执行第一种暴力,时间复杂度为 。否则 和 的度数都超过 ,则执行第二种暴力,时间复杂度为 。
可以取 或 ,都可以过,下面代码取 (会更快些)。
AC 代码如下,时间复杂度为 :
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 1e5 + 5, B = 320, mod = 998244353;
set<int> st[N];
int main() {
int n, m;
scanf("%d %d", &n, &m);
int t = 0;
map<int, bitset<N>> mp;
while (m--) {
int a, b, c;
scanf("%d %d %d", &a, &b, &c);
a = 1 + (a * (1ll + t) % mod) % 2;
b = 1 + (b * (1ll + t) % mod) % n;
c = 1 + (c * (1ll + t) % mod) % n;
if (a == 1) {
st[b].insert(c);
if (st[b].size() > B) {
if (!mp.count(b)) {
for (auto &x : st[b]) {
mp[b][x] = 1;
}
}
mp[b][c] = 1;
}
st[c].insert(b);
if (st[c].size() > B) {
if (!mp.count(c)) {
for (auto &x : st[c]) {
mp[c][x] = 1;
}
}
mp[c][b] = 1;
}
}
else {
if (st[b].size() > st[c].size()) swap(b, c);
if (st[b].size() <= B) {
t = 0;
for (auto &x : st[b]) {
if (st[x].count(c)) {
t = x;
break;
}
}
}
else {
bitset<N> bs = mp[b] & mp[c];
if (bs.any()) t = bs._Find_first();
else t = 0;
}
printf("%d\n", t);
}
}
return 0;
}
参考资料
AtCoder Beginner Contest 350:https://www.cnblogs.com/No-play-Yes-splay/p/18148245/atcoder-beginner-contest-350-sol
AtCoder Beginner Contest 350 A 至 G 題讲解 by dreamoon:https://www.bilibili.com/video/BV1Ub421Y7GZ/
本文来自博客园,作者:onlyblues,转载请注明原文链接:https://www.cnblogs.com/onlyblues/p/18175932
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
2023-05-06 D. Fish Graph
2023-05-06 D. Running Miles