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 <bits/stdc++.h>using namespace std;
#define ll long long
#define INF 0x3f3f3f3f
// #define DEBUG
map<int, int> mp; //映射回该作品的真正的ID
struct Cmp
{bool operator()(const pair<ll, int> & a,const pair<ll, int> & 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<int, ll> cnt; //纪录每个作品的票数
set <pair<ll, pair<ll, int> > > seg; //维护现有的段 <右端点,<左端点, 作品id> >
set <pair<ll, int>, 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<pair<ll, pair<ll, int> > > 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;
}
CCF CSP 202209-4 吉祥物投票【并查集+Set维护段】相关推荐
- 7-3 最小生成树-kruskal (10 分)(思路+详解+并查集详解+段错误超时解决)宝 Come
一:前言 本题需要用到并查集的知识,建议先学完并查集后再看看本题 二:题目 题目给出一个无向连通图,要求求出其最小生成树的权值. 温馨提示:本题请使用kruskal最小生成树算法. 输入格式: 第一行 ...
- CCF CSP 202209
第一题 如此编码 #include<stdio.h> #include<string.h> #include<algorithm>int a[10000]; lon ...
- 第27次CCF CSP(202209) T3非常详细题解 防疫大数据(C++)
-->原题链接 思路: 大模拟最重要的是理清思路,针对该题的几块内容来说. 对于地区:用map来存,用pair表示某个地区是风险区的连续的开始和结束时间.维护起来 ...
- 线段树分治 ---- F. Extending Set of Points(线段树分治 + 可撤销并查集)
题目链接 题目大意: 你有个点集合SSS,每次往集合里面加点或者删点(如果要加的点出现过),如果(x1,y1),(x2,y1),(x1,y2),(x2,y2)(x1,y1),(x2,y1),(x1,y ...
- 线段树分治 ---- CF1217F - Forced Online Queries Problem(假离线 可撤销并查集 + 线段树分治)详解
题目链接 题目大意 解题思路: 我一开始想到可以用可撤销并查集去维护这种删边加边的操作,但是有个缺点是每次撤销都有把后面的边全部撤销复度是O(n2)O(n^2)O(n2) 首先我们考虑这种动态加边删边 ...
- 0x41.数据结构进阶 - 并查集
目录 一.路径压缩与按秩合并 1.AcWing 237. 程序自动分析(NOIP2015) 二.边带权并查集 1.AcWing 238. 银河英雄传说(边带权并查集模板) 2.AcWing 239. ...
- P1525 关押罪犯(扩展域并查集/二分图)
洛谷题目链接 输入 4 6 1 4 2534 2 3 3512 1 2 28351 1 3 6618 2 4 1805 3 4 12884 输出 3512 1.并查集 有意思的一道并查集的题,需要一些 ...
- Codeforces Round #423 (Div. 2, rated, based on VK Cup Finals) C. String Reconstruction 并查集
C. String Reconstruction 题目连接: http://codeforces.com/contest/828/problem/C Description Ivan had stri ...
- BZOJ-1854-[Scoi2010]游戏(并查集)
Description lxhgww最近迷上了一款游戏,在游戏里,他拥有很多的装备,每种装备都有2个属性,这些属性的值用[1,10000]之间的数表示.当他使用某种装备时,他只能使用该装备的某一个属性 ...
最新文章
- framebuffer小程序显示3个矩形 测试
- 数控程序中r及q代表什么_邹军:如何利用数学公式编写cnc程序?
- Mysql存储过程和存储函数
- JS实现星星评分功能实例代码(两种方法)
- Java读写二维数组到文件
- CentOS6.7上使用FPM打包制作自己的rpm包
- linux android studio 快捷方式,Android studio中的代码格式化快捷方式
- 如何退出vim编辑器?
- Tomcat8.0进入tomcat manager的方法
- 2022_天勤数据结构高分笔记_第二章_算法
- 数学建模系列--模糊综合评价
- 网络攻防技术——环境变量与set-uid实验
- Oracle数据库的备份方法
- 阿里巴巴宣布传承计划,没有马云的阿里会如何?
- 5G系统——5G-GUTI、5G-TMSI、5G-S-TMSI、SUPI、SUCI
- LED的闪烁频率设定
- 读书笔记:《浪潮之巅:下》
- IDA ,ida pro专业操作手册
- 《无法打开包括文件:“Eigen\Dense”:No such file or directory》亲测有效的解决方法
- rk3288_5.1_BOX 调整HDMI屏幕满屏