“蔚来杯“2022牛客暑期多校训练营8

Equivalence in Connectivity

前置知识点如下:

1. 并查集哈希
2. 可撤销并查集
3. 线段树分治

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

#define IOS ios::sync_with_stdio(0),cin.tie(0)
#define endl '\n'

typedef long long ll;
typedef unsigned long long ull;

const int N = 2e5 + 10;

int k, n, m;

unordered_map<ll, set<pair<int, int>>>mp;  // 存储每条边出现的区间

struct edge{ int u, v, op; }a[N];

vector<int>G[N];

int in[N], out[N], pos[N], tim;
void dfs(int x){
    in[x] = ++tim;
    pos[tim] = x;
    for (auto y : G[x])dfs(y);
    out[x] = tim;
}

vector<pair<int, int>>tr[N << 2];

#define ls (p<<1)
#define rs (p<<1|1)

void update(int p, int pl, int pr, int l, int r, int u, int v){
    if (l <= pl && pr <= r){
        tr[p].emplace_back(u, v);
        return;
    }
    int mid = pl + pr >> 1;
    if (l <= mid)update(ls, pl, mid, l, r, u, v);
    if (r > mid)update(rs, mid + 1, pr, l, r, u, v);
}

mt19937_64 rnd(time(0));

int fa[N], sz[N];
ull val[N], sum;
stack<pair<int, int>>st;

void init(int n){
    for (int i = 1;i <= n;++i)
        fa[i] = i, sz[i] = 1, val[i] = rnd();
    sum = 0;
}

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

bool merge(int x, int y){
    x = find(x), y = find(y);
    if (x == y)return 0;
    if (sz[x] > sz[y])swap(x, y);
    st.push({ x,y });
    sum -= val[x] + val[y];
    fa[x] = y, sz[y] += sz[x], val[y] ^= val[x];
    sum += val[y];
    return 1;
}

unordered_map<ull, vector<int>>ans;
void query(int p, int pl, int pr){
    int num = 0;
    for (auto [u, v] : tr[p])
        num += merge(u, v);

    if (pl == pr)ans[sum].push_back(pos[pl]);
    else{
        int mid = pl + pr >> 1;
        query(ls, pl, mid);
        query(rs, mid + 1, pr);
    }

    while (num--){
        auto [x, y] = st.top();st.pop();
        sum -= val[y];
        fa[x] = x, sz[y] -= sz[x], val[y] ^= val[x];
        sum += val[x] + val[y];
    }
}

ll base = N * 100;

void solve(){

    cin >> k >> n >> m;

    mp.clear();
    for (int i = 1;i <= m;++i){
        int u, v;
        cin >> u >> v;
        mp[u * base + v].insert({ 1,k });
    }

    for (int i = 1;i <= k;++i)G[i].clear();
    for (int i = 2;i <= k;++i){
        string s;
        int fa, u, v;
        cin >> fa >> s >> u >> v;
        G[fa].push_back(i);
        a[i] = { u,v,s == "add" };
    }

    tim = 0; dfs(1);  // dfs序

    for (int i = 2;i <= k;++i){  // 维护边出现的区间集合
        ll now = a[i].u * base + a[i].v;
        auto& s = mp[now];
        if (a[i].op)s.insert({ in[i],out[i] });
        else{
            // 要删除的区间一定是set内某个区间的一部分
            auto it = --s.upper_bound({ out[i] + 1,0 });
            auto [l, r] = *it;
            s.erase(it);
            if (l < in[i])s.insert({ l,in[i] - 1 });
            if (r > out[i])s.insert({ out[i] + 1,r });
        }
    }

    for (int i = 1;i <= 4 * k;++i)tr[i].clear();  // 清空线段树
    for (auto [val, s] : mp){
        int u = val / base, v = val % base;
        for (auto [l, r] : s)
            update(1, 1, k, l, r, u, v);
    }

    ans.clear();
    init(n);  // 初始化并查集
    query(1, 1, k);  // 线段树分治
    cout << ans.size() << endl;
    for (auto [sum, res] : ans){
        cout << res.size() << ' ';
        for (auto x : res)cout << x << ' ';
        cout << endl;
    }
}

signed main(){
#ifdef FSLSE
    freopen("in.in", "r", stdin);
    freopen("out.out", "w", stdout);
#endif

    IOS;

    int T;
    cin >> T;
    while (T--){
        solve();
    }

    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值