您的位置:首页 > 其它

4000 【SJTUOJ笔记】P1118 Travel

2018-03-29 16:06 344 查看
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;
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: