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会产生一些段(即关于第到第
人投票给作品
),相比只维护每个人或者每个作品的信息复杂度都十分不能接受,所以这里通过一个set集合
维护段信息(具体以三元组形式保存
),并且以每段的右端点作为排序的基准。
操作1:我们根据二分查找到第一个有交集的段和最后一个有交集的段,然后把这些有交集的段都从集合中删掉,最后再把本次要插入的段加入到集合中,同时也要判断开头和结尾有交集的段是否覆盖完了,如果没有覆盖完,还要再把没有覆盖完的部分加入到集合中。
操作2:这是个合并操作,如果每次都是直接操作维护段信息的集合的信息,显然复杂度就不符合了,懒修改就是这种题的优化方向。这里实现懒修改的方式则是通过并查集。首先我们要使用数组p存储每个作品真正的(即现在第
个作品的
不一定就是
了)。此时要将
合并到
,即是我们通过并查集将
合并到
,然后在给
赋一个新的值
,方便后面继续维护作品
真正的票数。通过这种方式我们不需要真正的修改到每个段的信息,只需要维护每个作品真正所属的
就可以了。
操作3:则是直接交换和
即可。
操作4:对于每个作品维护一个数组,凡是有修改的操作(操作1,2,3),我们都按要求更新
即可。
操作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;
}
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
