题目描述

为了促进西西艾弗岛上的旅游业发展,当地决定设计一个吉祥物形象。活动吸引了众多设计领域的大师和爱好者参加,经过初步筛选,共选出了 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维护段】相关推荐

  1. 7-3 最小生成树-kruskal (10 分)(思路+详解+并查集详解+段错误超时解决)宝 Come

    一:前言 本题需要用到并查集的知识,建议先学完并查集后再看看本题 二:题目 题目给出一个无向连通图,要求求出其最小生成树的权值. 温馨提示:本题请使用kruskal最小生成树算法. 输入格式: 第一行 ...

  2. CCF CSP 202209

    第一题 如此编码 #include<stdio.h> #include<string.h> #include<algorithm>int a[10000]; lon ...

  3. 第27次CCF CSP(202209) T3非常详细题解 防疫大数据(C++)

    -->原题链接 思路:         大模拟最重要的是理清思路,针对该题的几块内容来说.         对于地区:用map来存,用pair表示某个地区是风险区的连续的开始和结束时间.维护起来 ...

  4. 线段树分治 ---- F. Extending Set of Points(线段树分治 + 可撤销并查集)

    题目链接 题目大意: 你有个点集合SSS,每次往集合里面加点或者删点(如果要加的点出现过),如果(x1,y1),(x2,y1),(x1,y2),(x2,y2)(x1,y1),(x2,y1),(x1,y ...

  5. 线段树分治 ---- CF1217F - Forced Online Queries Problem(假离线 可撤销并查集 + 线段树分治)详解

    题目链接 题目大意 解题思路: 我一开始想到可以用可撤销并查集去维护这种删边加边的操作,但是有个缺点是每次撤销都有把后面的边全部撤销复度是O(n2)O(n^2)O(n2) 首先我们考虑这种动态加边删边 ...

  6. 0x41.数据结构进阶 - 并查集

    目录 一.路径压缩与按秩合并 1.AcWing 237. 程序自动分析(NOIP2015) 二.边带权并查集 1.AcWing 238. 银河英雄传说(边带权并查集模板) 2.AcWing 239. ...

  7. P1525 关押罪犯(扩展域并查集/二分图)

    洛谷题目链接 输入 4 6 1 4 2534 2 3 3512 1 2 28351 1 3 6618 2 4 1805 3 4 12884 输出 3512 1.并查集 有意思的一道并查集的题,需要一些 ...

  8. 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 ...

  9. BZOJ-1854-[Scoi2010]游戏(并查集)

    Description lxhgww最近迷上了一款游戏,在游戏里,他拥有很多的装备,每种装备都有2个属性,这些属性的值用[1,10000]之间的数表示.当他使用某种装备时,他只能使用该装备的某一个属性 ...

最新文章

  1. framebuffer小程序显示3个矩形 测试
  2. 数控程序中r及q代表什么_邹军:如何利用数学公式编写cnc程序?
  3. Mysql存储过程和存储函数
  4. JS实现星星评分功能实例代码(两种方法)
  5. Java读写二维数组到文件
  6. CentOS6.7上使用FPM打包制作自己的rpm包
  7. linux android studio 快捷方式,Android studio中的代码格式化快捷方式
  8. 如何退出vim编辑器?
  9. Tomcat8.0进入tomcat manager的方法
  10. 2022_天勤数据结构高分笔记_第二章_算法
  11. 数学建模系列--模糊综合评价
  12. 网络攻防技术——环境变量与set-uid实验
  13. Oracle数据库的备份方法
  14. 阿里巴巴宣布传承计划,没有马云的阿里会如何?
  15. 5G系统——5G-GUTI、5G-TMSI、5G-S-TMSI、SUPI、SUCI
  16. LED的闪烁频率设定
  17. 读书笔记:《浪潮之巅:下》
  18. IDA ,ida pro专业操作手册
  19. 《无法打开包括文件:“Eigen\Dense”:No such file or directory》亲测有效的解决方法
  20. rk3288_5.1_BOX 调整HDMI屏幕满屏

热门文章

  1. 浏览器市场再起烽火 Mozilla能否撼动IE?
  2. 建造《流浪地球2》中要毁灭人类的超级量子计算机MOSS的核心量子技术是什么?
  3. 窗外的夜雨心中的思雨
  4. 智方6000系五金交电销售管理系统 卡巴斯基反病毒软件
  5. 与其羡慕他人智慧,不如自己勤奋补拙
  6. 中兴通讯 嵌入式开发工程师 校招一面面经
  7. excel赢得值曲线图_赢得价值$ 500的网站调整价值
  8. MicMac 命令集
  9. P1439 【模板】最长公共子序列(LCS+LIS)
  10. python 学习手册 一 基本语法