CCF CSP 202209-4 吉祥物投票【并查集+Set维护段】

题目描述

为了促进西西艾弗岛上的旅游业发展,当地决定设计一个吉祥物形象。活动吸引了众多设计领域的大师和爱好者参加,经过初步筛选,共选出了 m 个作品,编号为 1∼m,进行最终的投票角逐。

活动还吸引了西西艾弗岛上的 n 名投票者参与,编号为 1∼n,每人都在最终的投票环节拥有投一票的权利,也可以放弃投票。我们定义每个人的投票意愿 ai(1≤i≤n) 为一个 0∼m 的整数,若 ai=0 表示这个人目前没有支持的作品,打算放弃投票,否则表示这个人支持第 ai 号作品并有意愿将票投给它。

最初,由于所有人对参与竞选的作品都不了解,因此投票意愿 ai 均为 0。接下来是紧张刺激的拉票环节,作品的设计者们要想方设法给自己的作品拉票,这一过程中可能出现如下若干种事件:

1 l r x:编号为 x 的作品开展了一场拉票活动,成功地吸引了编号为 l∼r 的投票者的兴趣,使得他们的投票意愿全部改为 x。

2 x w:编号为 x 的作品需要经历一次大规模修改,所以需要暂时退出竞选。由于 x 与 w 两个作品的风格较为相近,因此原先投票意愿为 x 的投票者的投票意愿变为了 w。特别地,若 w=0,表示这些投票者暂时找不到新的支持的作品。需要注意的是,作品 x 退出竞选只是暂时的,因此后续的事件中作品 x 仍可能出现。

3 x y:主办方发现自己的统计出现了失误,将编号为 x 和 y 的作品弄颠倒了。发布勘误后,所有原先投票意愿为 x 的投票者的投票意愿变为了 y,所有原先投票意愿为 y 的投票者的投票意愿变为了 x。

4 w:主办方决定进行一次调查:希望知道所有投票者中,当前投票意愿为 w 的有多少人。若 w≠0 ,相当于调查有多少投票者目前支持作品 w ,否则相当于调查有多少投票者目前没有支持的作品。

5:主办方决定进行一次调查:若以现在的投票意愿进行最终的投票,获胜的作品是哪一个。规定得票数至少为 1 且最多的作品获胜,得票数相同则编号较小的作品获胜。特别地,若所有作品均无得票,认为不存在获胜作品。

从拉票开始到结束,共出现了 q 次如上的事件。由于参选的作品数和投票人数实在太多,单凭活动主办方的能力难以全面统计,现在请你编写一个程序来处理这些事件,并求出每次调查的结果。

输入格式

从标准输入读入数据。

第 1 行,3 个正整数 n,m,q。

接下来 q 行,每行 1∼4 个非负整数,描述一个事件。

输出格式

输出到标准输出。

对于每个 4 或 5 事件输出一行,一个非负整数表示此次调查的结果。其中事件 5 若不存在获胜作品则输出0

样例输入

10 2 15
5
1 2 4 1
1 4 7 2
4 1
5
1 3 4 1
5
1 7 9 1
3 1 2
4 2
2 1 2
4 2
2 2 0
4 2
5

样例输出

0
2
2
1
6
8
0
0

先看官方的讲解视频会更好理解本题做法。(下面代码是视频中的解法2)

因为进行操作1会产生一些段(即关于第i到第j人投票给作品k),相比只维护每个人或者每个作品的信息复杂度都十分不能接受,所以这里通过一个set集合seg维护段信息(具体以三元组形式保存(i, j, k)),并且以每段的右端点作为排序的基准。

操作1:我们根据l, r二分查找到第一个有交集的段和最后一个有交集的段,然后把这些有交集的段都从集合中删掉,最后再把本次要插入的段加入到集合中,同时也要判断开头和结尾有交集的段是否覆盖完了,如果没有覆盖完,还要再把没有覆盖完的部分加入到集合中。

操作2:这是个合并操作,如果每次都是直接操作维护段信息的集合的信息,显然复杂度就不符合了,懒修改就是这种题的优化方向。这里实现懒修改的方式则是通过并查集。首先我们要使用数组p存储每个作品真正的id(即现在第i个作品的id不一定就是i了)。此时要将x合并到w,即是我们通过并查集将p[x]合并到p[w],然后在给p[x]赋一个新的值z,方便后面继续维护作品x真正的票数。通过这种方式我们不需要真正的修改到每个段的信息,只需要维护每个作品真正所属的id就可以了。

操作3:则是直接交换p[x]p[y]即可。

操作4:对于每个作品维护一个cnt数组,凡是有修改的操作(操作1,2,3),我们都按要求更新cnt即可。

操作5:维护一个以票数和序号为排序基准的堆即可。

满分代码 

#include using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
// #define DEBUG
map mp;       //映射回该作品的真正的ID
struct Cmp
{bool operator()(const pair & a,const pair & b)const{if (a.first == b.first) return a.second < b.second;return a.first > b.first;}
};
int f[200010], p[200010];       //p[i]表示作品i真正的id。
int tot;
int finds(int x) { return f[x] == x ? x : finds(f[x]); }
void unions(int x, int y) { f[finds(x)] = finds(y); }
ll n;
int m, q;
map cnt;                               //纪录每个作品的票数
set  > > seg;            //维护现有的段 <右端点,<左端点, 作品id> >
set , Cmp> heap;                  //维护当前各个作品的得票数排名int main() {//freopen("4.in", "r", stdin);cin >> n >> m >> q;tot = m;for (int i = 1; i <= max(2 * q, 2 * m); i++) { f[i] = p[i] = i; }for (int i = 1; i <= m; i++) mp[i] = i;cnt[0] = n;seg.insert(make_pair(n, make_pair(1, 0)));for (int _ = 0; _ < q; _++) {int op;int x, y, w;ll l, r;cin >> op;if (op == 1) {cin >> l >> r >> x;int rx = p[x];auto bg = seg.lower_bound(make_pair(l, make_pair(0, rx)));      //找到插入本次段落的起点bg和终点edauto ed = seg.lower_bound(make_pair(r, make_pair(0, rx)));if (bg == seg.end()) {// auto tmp = heap.find(make_pair(cnt[x], x));// if (tmp != heap.end()) heap.erase(tmp);// cnt[x] += (r - l + 1);// heap.insert(make_pair(cnt[x], x));// seg.insert(make_pair(r, make_pair(l, x)));} else {ll b; ll a;int c, rc;vector > > add;for (auto it = bg; it != seg.end(); it++) {                 //删除bg和ed之间的段(因为都被本次插入覆盖了)b = (*it).first; a = (*it).second.first;c = (*it).second.second;rc = finds(c);   //找到c真正所属的作品的id#ifdef DEBUG// cout << a << ' ' << b << ' ' << c << '\n';#endif//更新信息(先删除,再插入)heap.erase(make_pair(cnt[rc], rc));cnt[rc] -= (b - a + 1);if (rc && cnt[rc] > 0) {heap.insert(make_pair(cnt[rc], rc));}if (it == ed) break;}b = (*bg).first; a = (*bg).second.first;c = (*bg).second.second;rc = finds(c);if (a <= l - 1 && l <= b) {     //考虑因为开头没有覆盖完,而新增加的段heap.erase(make_pair(cnt[rc], rc));cnt[rc] += (l - a);if (rc && cnt[rc] > 0)heap.insert(make_pair(cnt[rc], rc));add.push_back(make_pair(l - 1, make_pair(a, rc)));}if (ed != seg.end()) {      //考虑结尾没有覆盖完,而新增加的段b = (*ed).first; a = (*ed).second.first;c = (*ed).second.second;rc = finds(c);if (a <= r  && r + 1 <= b) {heap.erase(make_pair(cnt[rc], rc));cnt[rc] += (b - r);if (rc && cnt[rc] > 0)heap.insert(make_pair(cnt[rc], rc));add.push_back(make_pair(b, make_pair(r + 1, rc)));}}if (ed != seg.end()) ed = next(ed);seg.erase(bg, ed);for (auto ad: add) seg.insert(ad);seg.insert(make_pair(r, make_pair(l, rx)));heap.erase(make_pair(cnt[rx], rx));cnt[rx] += (r - l + 1);heap.insert(make_pair(cnt[rx], rx));#ifdef DEBUGcout << "set: " << '\n';for (auto x : seg) {cout << x.first << ' ' << x.second.first << ' ' << x.second.second << '\n';}cout << '\n';#endif}} else if (op == 2) {cin >> x >> w;heap.erase(make_pair(cnt[p[x]], p[x]));heap.erase(make_pair(cnt[p[w]], p[w]));cnt[p[w]] += cnt[p[x]];if (p[w]) heap.insert(make_pair(cnt[p[w]], p[w]));unions(p[x], p[w]);     //将x合并到w中p[x] = ++tot;           //给x一个新身份id(原来的已经被合并到w了,再用就会出错)mp[tot] = x;} else if (op == 3) {cin >> x >> y;swap(mp[p[x]], mp[p[y]]);swap(p[x], p[y]);} else if (op == 4) {cin >> w;w = p[w];cout << cnt[w] << '\n';} else {if (heap.size() == 0) cout << 0 << '\n';else {int ans = 0; int ans_id = 0;for (auto it: heap) {if (it.first > ans) {ans = it.first;ans_id = mp[it.second];}if (it.first == ans && mp[it.second] < ans_id) ans_id = mp[it.second];if (ans > it.first) break;}cout << ans_id << '\n';}}}return 0;
}


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部