【SJTUOJ笔记】P1118 Travel

本文介绍了一种使用并查集解决特定问题的方法,并通过引入懒惰传播思想来优化性能。具体展示了如何在合并集合时维护额外信息,以及如何在查询时更新状态。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

https://acm.sjtu.edu.cn/OnlineJudge/problem/1118
由于题目含有大量的合并和查询操作,很容易想到用并查集来做。关键在于,如何在合并集合的同时维护题目要求的信息。
并查集的特点是,任何操作都可以变化为对集合根的操作。但反过来,却无法把根的状态改变直接反映到下面的每个集合元素中。在本题中,具体体现就是一堆旅行团会被根带着一起旅行,但无法在近似 O(1) O ( 1 ) 的时间修改每个旅行团已经走过的城市个数。因此,这里采用了类似于线段树中lazy标记的思想,先记录根结点带着下面的旅行团走过了几个城市,合并时再下推标记,把 O(n) O ( n ) 的下推操作的次数尽可能减少。

具体如下:

void merge(int x, int y){
    int p = find(x), q = find(y);
    sum[q] += sum[p]; //sum[k]记录k号原始旅行团所在的大旅行团包含多少原始旅行团
    for (int i = 1; i <= n; ++i){ //下推标记
        int t = find(i);
        if (t == p || t == q)
            trl[i] += ex[t]; //ex[k]记录k号原始旅行团所在的大旅行团在合并前走过了多少城市
    }
    ex[p] = ex[q] = 0;
    parent[p] = q;
}
case 'T':
            cin >> x >> y;
            p = find(x); 
            q = find(y); //找到x和y的根
            if (p != q){
                a = nowat[p]; //nowat[k]记录k号原始旅行团在哪个城市
                b = nowat[q]; 
                now[a] = -1; //now[k]记录k号城市的大旅行团根的编号,-1表示没有旅行团在k号城市
                nowat[p] = b;
                ++ex[p]; //加标记
                merge(y, x);
            }
            break;
        case 'Q':
            cin >> x;
            p = find(x);
            cout << nowat[p] << ' ' << sum[p] << ' ' << trl[x] + ex[p] << '\n'; //trl[k]记录k号原始旅行团走过了几个城市
            break;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值