P3201 [HNOI2009]梦幻布丁
题目描述
N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色.例如颜色分别为1,2,2,1的四个布丁一共有3段颜色.
输入输出格式
输入格式:
第一行给出N,M表示布丁的个数和好友的操作次数.
第二行N个数A1,A2...An表示第i个布丁的颜色从第三行起有M行,对于每个操作,若第一个数字是1表示要对颜色进行改变,其后的两个整数X,Y表示将所有颜色为X的变为Y,X可能等于Y.
若第一个数字为2表示要进行询问当前有多少段颜色,这时你应该输出一个整数. 0
输出格式:
针对第二类操作即询问,依次输出当前有多少段颜色.
输入输出样例
4 3
1 2 2 1
2
1 2 1
2
3
1
说明
1<=n,m<=100,000; 0<Ai,x,y<1,000,000
Solution:
本题平衡树+启发式合并。
考试的时候没有注意$x==y$的情况,结果只对了8个点,GG。
思路比较简单,我们用多棵平衡树维护每种颜色的下标,在对一种颜色的平衡树进行操作的同时可以处理出这个颜色的连续段数,具体来说,对于一种颜色,若新加入一个节点,就判断该节点能否与其前趋或者后继相接,讨论一下各种情况就好了。
在合并的时候用启发式的思想把节点少的树的每个节点暴力加入节点多的树中,若$size$少的树是变换后的颜色所在树,直接交换两种颜色的树根就可以了。
查询就直接输出全局的答案。
时间复杂度:$O(n\log ^2 n)$
(坑点:$x==y$时不需要操作)
代码:
/*Code by 520 -- 10.25*/
#include<bits/stdc++.h>
#pragma GCC optimize(2)
#define il inline
#define ll long long
#define RE register
#define For(i,a,b) for(RE int (i)=(a);(i)<=(b);(i)++)
#define Bor(i,a,b) for(RE int (i)=(b);(i)>=(a);(i)--)
#define debug printf("%s %d\n",__FUNCTION__,__LINE__)
using namespace std;
const int N=1e6+;
int n,m,root[N],ans,sum[N];
int col[N],ch[N][],cnt,fa[N],rnd[N],siz[N],date[N]; int gi(){
int a=;char x=getchar();
while(x<''||x>'') x=getchar();
while(x>=''&&x<='') a=(a<<)+(a<<)+(x^),x=getchar();
return a;
} il int newnode(int v){
++cnt;
date[cnt]=v,rnd[cnt]=rand(),siz[cnt]=,fa[cnt]=;
return cnt;
} il void up(int rt){
if(ch[rt][]) fa[ch[rt][]]=rt;
if(ch[rt][]) fa[ch[rt][]]=rt;
siz[rt]=siz[ch[rt][]]+siz[ch[rt][]]+;
} int merge(int x,int y){
if(!x||!y) return x+y;
if(rnd[x]<rnd[y]) {ch[x][]=merge(ch[x][],y),up(x);return x;}
else {ch[y][]=merge(x,ch[y][]),up(y);return y;}
} void split(int rt,int v,int &x,int &y){
if(!rt) {x=y=;return ;}
if(date[rt]>v) y=rt,split(ch[rt][],v,x,ch[y][]),up(y);
else x=rt,split(ch[rt][],v,ch[x][],y),up(x);
} il int kth(int rt,int v){
while(){
if(siz[ch[rt][]]>=v) rt=ch[rt][];
else if(siz[ch[rt][]]+<v) v-=siz[ch[rt][]]+,rt=ch[rt][];
else return rt;
}
} il int pre(int id,int v){
int x,y,ans; split(root[id],date[v]-,x,y);
ans=(!siz[x])?-:(kth(x,siz[x]));
root[id]=merge(x,y),fa[root[id]]=;
return ans;
} il int suc(int id,int v){
int x,y,ans; split(root[id],date[v],x,y);
ans=(!siz[y])?-:(kth(y,));
root[id]=merge(x,y),fa[root[id]]=;
return ans;
} il void ins(int id,int v){
int x=pre(id,v),y=suc(id,v);
if(x>&&y>&&date[x]==date[v]-&&date[y]==date[v]+) ans--,sum[id]--;
else if(x<&&y<||x>&&date[x]!=date[v]-&&(y<||date[y]!=date[v]+)||y>&&date[y]!=date[v]+&&(x<||date[x]!=date[v]-)) ans++,sum[id]++;
x=y=;
split(root[id],date[v]-,x,y),root[id]=merge(merge(x,v),y),fa[root[id]]=;
} void dfs(int id,int rt){
if(!rt) return;
dfs(id,ch[rt][]),dfs(id,ch[rt][]);
ch[rt][]=ch[rt][]=;
ins(id,rt);
} int main(){
n=gi(),m=gi(); int opt,x,y;
For(i,,n) {
col[i]=gi();
ins(col[i],newnode(i));
}
while(m--){
opt=gi();
if(opt==) {
x=gi(),y=gi();
if(x==y) continue;
else if(siz[root[x]]<=siz[root[y]]){
dfs(y,root[x]),root[x]=;ans-=sum[x],sum[x]=;
}
else {
swap(root[x],root[y]),swap(sum[x],sum[y]);
dfs(y,root[x]),root[x]=;ans-=sum[x],sum[x]=;
}
}
else printf("%d\n",ans);
}
return ;
}
P3201 [HNOI2009]梦幻布丁的更多相关文章
- 洛谷P3201 [HNOI2009]梦幻布丁 [链表,启发式合并]
题目传送门 梦幻布丁 题目描述 N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色.例如颜色分别为1,2,2,1的四个布丁一共有3段颜色. 输入输 ...
- 洛谷P3201 [HNOI2009]梦幻布丁(链表 + 启发式合并)
题目链接 给出 \(n\) 个布丁,每个补丁都有其颜色.现在有 \(m\) 次操作,每次操作将第 \(x_i\) 种颜色全部变为第 \(y_i\) 种颜色. 操作中可能会插入询问,回答目前总共有多少段 ...
- 洛谷P3201 [HNOI2009]梦幻布丁
题目描述 N个布丁摆成一行,进行M次操作.每次将某个颜色的布丁全部变成另一种颜色的,然后再询问当前一共有多少段颜色.例如颜色分别为1,2,2,1的四个布丁一共有3段颜色. 输入输出格式 输入格式: 第 ...
- luogu P3201 [HNOI2009]梦幻布丁
传送门 先考虑暴力,显然每次是把一个位置集合和另一个集合合并,同时维护答案,合并的过程中如果两个集合每有一对元素相邻,答案就减1 优化暴力的话,说到合并,怎么能不想起启发式合并呢?每次把一个大小小的集 ...
- 洛谷 P3201 [HNOI2009]梦幻布丁(启发式合并)
题面 luogu 题解 什么是启发式合并? 小的合并到大的上面 复杂度\(O(nlogn)\) 这题颜色的修改,即是两个序列的合并 考虑记录每个序列的\(size\) 小的合并到大的 存序列用链表 但 ...
- bzoj 1483 [HNOI2009]梦幻布丁(链表+启发式合并)
1483: [HNOI2009]梦幻布丁 Time Limit: 10 Sec Memory Limit: 64 MBSubmit: 1818 Solved: 761[Submit][Status ...
- BZOJ 1483: [HNOI2009]梦幻布丁( 链表 + 启发式合并 )
把相同颜色的串成一个链表, 然后每次A操作就启发式合并, 然后计算对答案的影响. ----------------------------------------------------------- ...
- BZOJ 1483: [HNOI2009]梦幻布丁 [链表启发式合并]
1483: [HNOI2009]梦幻布丁 题意:一个带颜色序列,一种颜色合并到另一种,询问有多少颜色段 一种颜色开一个链表,每次遍历小的合并到大的里,顺带维护答案 等等,合并方向有规定? 令col[x ...
- [HNOI2009] 梦幻布丁
[HNOI2009] 梦幻布丁 标签: 链表 题解 可以直接用链表启发式合并做. 合并的细节处理稍微有点麻烦. 假如需要变成另一种颜色的那个颜色的个数更多,那么就肯定不能直接合. 维护一个color数 ...
随机推荐
- Swift开发iOS项目实战视频教程(二)---图片与动画
本课主要介绍UIImageview.NSTimer的使用.并介绍了一种动画实现方式. 本教程摒弃枯燥的语法和知识解说.全是有趣有料的项目实战! 视频优酷链接:v.youku.com/v_show/id ...
- VMware Ubuntu蓝屏问题解决
解决方法: 问题分析启动 Ubuntu 可以进入登录界面,说明系统是可以运行起来的.没有发生大块的核心数据损坏,linux 系统一般都可以修复,一定要淡定.于是开始放狗(google)搜索.“VMwa ...
- 数列分块入门九题(一):LOJ6277~6279
Preface 分块,一个神奇的暴力算法.可以把很多\(O(n^2)\)的数据结构题的暴力优化到常数极小的\(O(n\sqrt n)\).当一些毒瘤题无法用线段树,主席树,平衡树,树状数组...... ...
- [spark][python]Spark map 处理
map 就是对一个RDD的各个元素都施加处理,得到一个新的RDD 的过程 [training@localhost ~]$ cat names.txtYear,First Name,County,Sex ...
- Intellij Idea 返回上次编辑快捷键设置
由于默认的返回上次编辑快捷键和和笔记本冲突. 需要从新设置快捷键. 找了好久终于找到了. 分别选中Back和Forward后设置新的快捷键即可
- 调用不同目录类的protected构造器
一.问题 二.分析 调用不同目录类的protected构造器,IDE报错. 二.解决办法: 后面添加一个{}就可以了
- Shell编程基础篇-上
1.1 前言 1.1.1 为什么学Shell Shell脚本语言是实现Linux/UNIX系统管理及自动化运维所必备的重要工具, Linux/UNIX系统的底层及基础应用软件的核心大都涉及Shell脚 ...
- Linux Namespace : PID
PID namespace 用来隔离进程的 PID 空间,使得不同 PID namespace 里的进程 PID 可以重复且互不影响.PID namesapce 对容器类应用特别重要, 可以实现容器内 ...
- Linux下开源邮件系统Postfix+Extmail+Extman环境部署记录
一.基础知识梳理MUA (Mail User Agent) MUA 既是"邮件使用者代理人",因为除非你可以直接利用类似 telnet 之类的软件登入邮件主机来主动发出信件,否则您 ...
- 网易2018.03.27算法岗,三道编程题100%样例AC题解
博主欢迎转载,但请给出本文链接,我尊重你,你尊重我,谢谢~http://www.cnblogs.com/chenxiwenruo/p/8660814.html特别不喜欢那些随便转载别人的原创文章又不给 ...