G - Mediator

onlyblues發表於2024-05-06

G - Mediator

Problem Statement

Beware the special input format and the smaller memory limit than usual.

There is an undirected graph with vertices $1, 2, \dots, N$, initially without edges.
You need to process the following $Q$ queries on this graph:

1 u v

Type $1$: Add an edge between vertices $u$ and $v$.
Before adding the edge, $u$ and $v$ belong to different connected components (i.e., the graph always remains a forest).

2 u v

Type $2$: If there is a vertex adjacent to both $u$ and $v$, report its vertex number; otherwise, report $0$.
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 $A, B, C$, and the encrypted query is given as three integers $a, b, c$.
Let $X_k$ be the answer to the $k$-th type-$2$ query. Define $X_k = 0$ for $k = 0$.
Restore $A, B, C$ from the given $a, b, c$ as follows:

  • Let $l$ be the number of type-$2$ queries given before this query (not counting the query itself). Then, use the following:
    • $A = 1 + (((a \times (1+X_l)) \mod 998244353) \mod 2)$
    • $B = 1 + (((b \times (1+X_l)) \mod 998244353) \mod N)$
    • $C = 1 + (((c \times (1+X_l)) \mod 998244353) \mod N)$

Constraints

  • All input values are integers.
  • $2 \le N \le 10^5$
  • $1 \le Q \le 10^5$
  • $1 \le u < v \le N$
  • $0 \le a,b,c < 998244353$

Input

The input is given from Standard Input in the following format:

$N$ $Q$
$\rm{Query}$$_1$
$\rm{Query}$$_2$
$\vdots$
$\rm{Query}$$_Q$

Output

Let $k$ be the number of type-$2$ queries. Print $k$ lines.
The $i$-th line should contain the answer to the $i$-th type-$2$ 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 $6$-vertex graph and $12$ queries.

  • The first query is 2 1 3.
    • No vertex is adjacent to both vertex $1$ and vertex $3$, so report $0$.
  • The second query is 1 2 6.
    • Add an edge between vertices $2$ and $6$.
  • The third query is 1 2 4.
    • Add an edge between vertices $2$ and $4$.
  • The fourth query is 1 1 3.
    • Add an edge between vertices $1$ and $3$.
  • The fifth query is 2 4 6.
    • The vertex adjacent to both vertices $4$ and $6$ is vertex $2$.
  • The sixth query is 2 1 4.
    • No vertex is adjacent to both vertices $1$ and $4$, so report $0$.
  • The seventh query is 1 5 6.
    • Add an edge between vertices $5$ and $6$.
  • The eighth query is 1 1 2.
    • Add an edge between vertices $1$ and $2$.
  • The ninth query is 2 1 4.
    • The vertex adjacent to both vertices $1$ and $4$ is vertex $2$.
  • The tenth query is 2 2 5.
    • The vertex adjacent to both vertices $2$ and $5$ is vertex $6$.
  • The eleventh query is 2 3 4.
    • No vertex is adjacent to both vertices $3$ and $4$, so report $0$.
  • The twelfth query is 2 2 3.
    • The vertex adjacent to both vertices $2$ and $3$ is vertex $1$.

Sample Input 2

2 1
377373366 41280240 33617925

Sample Output 2

 

The output may be empty.

解題思路

  容易想到的暴力思路是,給每個節點開一個 std::set 儲存鄰點。對於詢問 $(u,v)$,列舉 $u$ 的每個鄰點 $x$ 並判斷 $x$ 是否為 $v$ 的鄰點。一次詢問的時間複雜度為 $O(n \log{n})$。

  $q$ 個詢問肯定會超時。我們本質是想知道 $u$ 與 $v$ 的鄰點是否有交集,為此可以給每個節點開一個 std::bitset 標記其鄰點(如果與節點 $x$ 相鄰則第 $x$ 位置為 $1$)。對於詢問 $(u,v)$ 則只需將對應的兩個 std::bitset 按位與然後找到為 $1$ 的位置(或報告沒有)。一次詢問的時間複雜度為 $O\left(\frac{n}{w}\right)$,在 $3s$ 的時限內是可以透過的,但空間複雜度是 $O\left(n^2\right)$,顯然會爆掉。

  當我們想到兩種暴力做法時可以嘗試根號分治了,由於是圖論的問題,一般根據節點的輕重來分類,參考交友問題。設定一個閾值 $b$,當節點的度數超過 $b$ 時(也就是重點),就給這個節點開一個 std::bitset。容易知道重點的數量大致是 $O\left(\frac{n}{b}\right)$ 級別的,因此 std::bitset 的空間複雜度就是 $O\left(\frac{n^2}{b}\right)$。

  由於是強制線上,所以在加邊的過程中需要動態維護節點的度數,當加完變後節點的度數超過 $b$,就需要為該節點開一個 std::bitset,然後列舉 std::set 中的節點進行標記。在所有的詢問中,總計算量是 $O\left(b \cdot \frac{n}{b}\right)$。

  對於詢問 $(u,v)$,不失一般性假設 $u$ 比 $v$ 的度數小(否則可以交換 $u$ 和 $v$ 而不影響結果)。如果 $u$ 的度數不超過 $b$,則執行第一種暴力,時間複雜度為 $O(b \log{n})$。否則 $u$ 和 $v$ 的度數都超過 $b$,則執行第二種暴力,時間複雜度為 $O\left(\frac{n}{w}\right)$。

  可以取 $b = \sqrt{n}$ 或 $b = \frac{n}{w \cdot log{n}}$,都可以過,下面程式碼取 $b = \sqrt{n}$(會更快些)。

  AC 程式碼如下,時間複雜度為 $O\left(q \sqrt{n} \log{n} + q \frac{n}{w}\right)$:

#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/

相關文章