题解:P8149 泪光 | Tears

Problem Link

泪光 | Tears

题意

通过函数的基本知识可以简化题意:

操作 $ 1 $:若 \(a\)\(b\) 在一个块中,使 \(c\) 并入 \(d\) 块中。

操作 $ 2 $:使 \(a\) 并入 \(b\) 块中。

操作 $ 3 $:询问 \(a\) 是否与 \(b\) 在一个块中。

操作 $ 4 $:询问 \(a\) 的块大小。

Solution

不难想到并查集可做。

但观察到题目中一句话“你需要根据当前已知的条件作出判断”。也就是说每次操作 $ 2 $ 可能会使操作 $ 1 $ 条件满足,进行完操作后使其他的操作 $ 1 $ 条件满足……

比如说这组样例:

6 9
1 1 2 3 4
1 3 4 5 6
3 1 2
3 3 4
3 5 6
2 1 2
3 1 2
3 3 4
3 5 6

这样的话,很遗憾直接并查集复杂度会变成 \(O(n^2)\)

如何优化呢?

在学并查集的时候,我们知道,并查集有两种优化方法:路经压缩和启发式合并,路经压缩十分自然,那么启发式合并呢?

考虑给每个不满足条件的操作 $ 1 $ 打上标记,标记带着 \(a,b,c,d\),把标记放入一个 vector 中,在启发式合并时枚举小的 vector,去有较多标记的块中看是否有带 \(b\) 的标记。如果有,将标记的 \(c,d\) 放入一个操作栈中(不要删除,因为 vector 删除中间的元素是 \(O(n)\) 的);若没有,直接将标记整个放入 vector 中即可。

这样由于标记至多只有 \(m\) 个,不删除复杂度是 \(O(n)\),算上启发式的 \(\log n\),总复杂度为 \(O(n \log n)\)

Code

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;
#define Maxn 6000005
#define fo(i, l, r) for (int i = l; i <= r; ++i)
#define fr(i, r, l) for (int i = l; i >= r; --i)
// #define getchar()(p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
// char buf[1<<21], *p1 = buf, *p2 = buf;
inline int read(int x=0, bool f=0, char c=getchar()) {for(;!isdigit(c);c=getchar()) f^=!(c^45);for(;isdigit(c);c=getchar()) x=(x<<1)+(x<<3)+(c^48);return f?-x:x;}
inline ll lread(ll x=0, bool f=0, char c=getchar()) {for(;!isdigit(c);c=getchar()) f^=!(c^45);for(;isdigit(c);c=getchar()) x=(x<<1)+(x<<3)+(c^48);return f?-x:x;}

int n, m, fa[Maxn], siz[Maxn], tag_to_point[Maxn];

struct Tag
{
    int a, b, c, d;
    Tag() {}
    Tag(int a, int b, int c, int d) : a(a), b(b), c(c), d(d) {}
};

vector<Tag> tag[Maxn];
stack<Tag> stk;

int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); }

void merge(int x, int y)
{
    int fx = find(x), fy = find(y);
    if( fx == fy ) return ;
    if(tag_to_point[fx] > tag_to_point[fy]) swap(fx, fy);
    for(Tag x : tag[fx])
    {
        if(find(x.b) == fy) stk.push(Tag(x.c, x.d, 0, 0));
        else tag[fy].push_back(x), ++tag_to_point[fy];
    } 
    fa[fx] = fy;
    siz[fy] += siz[fx], siz[fx] = 0;
}

int query(int x) { return siz[find(x)]; }

void Init() { fo(i, 1, n) fa[i] = i, siz[i] = 1, tag_to_point[i] = 0; }

int main()
{
    n = read(), m = read();
    Init();
    fo(i, 1, m) 
    {
        int op = read();
        if(op == 1) 
        {
            int a = read(), b = read(), c = read(), d = read(), fa = find(a), fb = find(b);
            if(fa == fb) merge(c, d);
            else ++tag_to_point[fa], ++tag_to_point[fb], tag[fa].push_back(Tag(a, b, c, d)), tag[fb].push_back(Tag(b, a, c, d));
        }
        else if(op == 2) {int a = read(), b = read(); merge(a, b);}
        else if(op == 3) {int a = read(), b = read(); puts(find(a) == find(b) ? "entangled" : "separate");}
        else {int a = read(); printf("%d\n", query(a));}
        while( ! stk.empty() ) {Tag x = stk.top(); stk.pop(); merge(x.a, x.b);}
    }
    return 0;
}

Tips

记得初始化。

posted @ 2024-10-15 08:28  naughty_Naught  阅读(10)  评论(0编辑  收藏  举报