BZOJ5011 [JXOI2017]颜色 【线段树 + 主席树】
题目链接
题解
一定只有我这种智障会用这么奇怪的方法做这道题。。
由题我们知道最后剩余的一定是一个区间,而且区间内的颜色不存在于区间外
所以我们的目的就是为了找到这样的区间的数量
区间由左右端点确定,我们枚举右端点,尝试维护左端点数量
当我们从右向左枚举到\(r\),\(r\)右边的颜色已经在区间外,一定不能被包含入区间,所以我们记录每个位置上一个同色位置\(pre[i]\),指针\(r\)每越过一个位置,就在\(pre[i]\)处打一个标记,表示这个位置不能被包含。之后我们每次找到\(r\)左边第一个标记的位置,再往左一定不合法,这样我们就得到了我们的初始区间,记为\([l,r]\)。这个操作可以使用线段树维护
考虑这个区间还会有什么不满足的地方
就是区间内的颜色不能出现在区间外
对于一个在\(r\)左侧的位置\(i\),颜色为\(c\),记颜色\(c\)最左边的位置为\(p\),那么\([p + 1,i]\)都不能被选择,因为选择其中任意一个位置,都会使颜色\(c\)被分割
如何维护?
另开一个线段树区间\(set\)为\(1\)即可,一个位置可能被多个区间叠加,但只能贡献\(1\)
由于区间\(set\)操作不能被撤回,所以我们更新顺序只能从左到右,但我们是从右到左枚举的,怎么办?
那就强行从左到右更新,并保存每一个版本的线段树——永久化标记主席树
这样我们就做完了这道题
复杂度\(O(nlogn)\)
#include<algorithm>
#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>
#include<map>
#define Redge(u) for (int k = h[u],to; k; k = ed[k].nxt)
#define REP(i,n) for (int i = 1; i <= (n); i++)
#define mp(a,b) make_pair<int,int>(a,b)
#define cls(s) memset(s,0,sizeof(s))
#define cp pair<int,int>
#define LL long long int
using namespace std;
const int maxn = 300005,maxm = 8000005,INF = 1000000000;
inline int read(){
int out = 0,flag = 1; char c = getchar();
while (c < 48 || c > 57){if (c == '-') flag = -1; c = getchar();}
while (c >= 48 && c <= 57){out = (out << 3) + (out << 1) + c - 48; c = getchar();}
return out * flag;
}
int ls[maxm],rs[maxm],Sum[maxm],tag[maxm],rt[maxn],cnt;
void Upd(int u){
Sum[u] = Sum[ls[u]] + Sum[rs[u]];
}
void Set(int& u,int pre,int l,int r,int L,int R){
u = ++cnt; ls[u] = ls[pre]; rs[u] = rs[pre]; tag[u] = Sum[u] = 0;
tag[u] = tag[pre];
if (tag[u]){
Sum[u] = r - l + 1;
return;
}
if (l >= L && r <= R){
tag[u] = 1; Sum[u] = r - l + 1;
return;
}
int mid = l + r >> 1;
if (mid >= L) Set(ls[u],ls[pre],l,mid,L,R);
if (mid < R) Set(rs[u],rs[pre],mid + 1,r,L,R);
Upd(u);
}
int Query(int u,int l,int r,int L,int R){
if (tag[u]) return R - L + 1;
if (l >= L && r <= R) return Sum[u];
int mid = l + r >> 1;
if (mid >= R) return Query(ls[u],l,mid,L,R);
if (mid < L) return Query(rs[u],mid + 1,r,L,R);
return Query(ls[u],l,mid,L,mid) + Query(rs[u],mid + 1,r,mid + 1,R);
}
int sum[maxn << 2],mp[maxn << 2];
void upd(int u,int l,int r){
sum[u] = sum[u << 1] + sum[u << 1 | 1];
int mid = l + r >> 1;
if (mp[u << 1 | 1] == 0) mp[u] = 0;
else if (mp[u << 1] == 0) mp[u] = mp[u << 1 | 1];
else if (mp[u << 1 | 1] == mid + 1) mp[u] = mp[u << 1];
else mp[u] = mp[u << 1 | 1];
}
void modify(int u,int l,int r,int pos){
if (l == r) {sum[u] = 1; mp[u] = 0; return;}
int mid = l + r >> 1;
if (mid >= pos) modify(u << 1,l,mid,pos);
else modify(u << 1 | 1,mid + 1,r,pos);
upd(u,l,r);
}
int query(int u,int l,int r,int L,int R){
if (!sum[u]) return l;
if (l >= L && r <= R) return mp[u];
int mid = l + r >> 1;
if (mid >= R) return query(u << 1,l,mid,L,R);
if (mid < L) return query(u << 1 | 1,mid + 1,r,L,R);
int t1 = query(u << 1,l,mid,L,R),t2 = query(u << 1 | 1,mid + 1,r,L,R);
if (t2 == 0) return 0;
else if (t1 == 0) return t2;
else if (t2 == mid + 1) return t1;
else return t2;
}
void build(int u,int l,int r){
sum[u] = 0;
if (l == r){
mp[u] = l;
return;
}
int mid = l + r >> 1;
build(u << 1,l,mid);
build(u << 1 | 1,mid + 1,r);
upd(u,l,r);
}
int last[maxn],pre[maxn],F[maxn];
int n,A[maxn];
int main(){
int T = read();
while (T--){
n = read(); REP(i,n) A[i] = read();
cnt = 0; build(1,1,n);
for (int i = 1; i <= n; i++) last[i] = 0;
for (int i = 1; i <= n; i++){
if (!last[A[i]]) F[A[i]] = i;
pre[i] = last[A[i]];
last[A[i]] = i;
rt[i] = rt[i - 1];
if (F[A[i]] < i) Set(rt[i],rt[i - 1],1,n,F[A[i]] + 1,i);
}
LL ans = 0;
for (int i = n; i; i--){
int l = query(1,1,n,1,i),r = i;
if (pre[i]) modify(1,1,n,pre[i]);
if (!l) continue;
int t = Query(rt[i],1,n,l,r);
ans += (r - l + 1) - t;
}
printf("%lld\n",ans);
}
return 0;
}
BZOJ5011 [JXOI2017]颜色 【线段树 + 主席树】的更多相关文章
- 线段树简单入门 (含普通线段树, zkw线段树, 主席树)
线段树简单入门 递归版线段树 线段树的定义 线段树, 顾名思义, 就是每个节点表示一个区间. 线段树通常维护一些区间的值, 例如区间和. 比如, 上图 \([2, 5]\) 区间的和, 为以下区间的和 ...
- BZOJ5011 JXOI2017颜色(主席树)
相当于求满足在子段中出现的颜色只在该子段中出现的非空子段数量.这也就相当于其中出现的颜色最左出现的位置在左端点右侧,最右出现的位置在右端点左侧.那么若固定某个端点,仅考虑对该端点的限制,会有一段合法区 ...
- 线段树(单标记+离散化+扫描线+双标记)+zkw线段树+权值线段树+主席树及一些例题
“队列进出图上的方向 线段树区间修改求出总量 可持久留下的迹象 我们 俯身欣赏” ----<膜你抄> 线段树很早就会写了,但一直没有总结,所以偶尔重写又会懵逼,所以还是要总结一下. ...
- 学习笔记--函数式线段树(主席树)(动态维护第K极值(树状数组套主席树))
函数式线段树..资瓷 区间第K极值查询 似乎不过似乎划分树的效率更优于它,但是如果主席树套树状数组后,可以处理动态的第K极值.即资瓷插入删除,划分树则不同- 那么原理也比较易懂: 建造一棵线段树(权值 ...
- UOJ#218. 【UNR #1】火车管理 线段树 主席树
原文链接https://www.cnblogs.com/zhouzhendong/p/UOJ218.html 题解 如果我们可以知道每次弹出栈之后新的栈顶是什么,那么我们就可以在一棵区间覆盖.区间求和 ...
- Luogu5289 十二省联考2019字符串问题(后缀数组+拓扑排序+线段树/主席树/KDTree)
先考虑80分做法,即满足A串长度均不小于B串,容易发现每个B串对应的所有A串在后缀数组上都是一段连续区间,线段树优化连边然后判环求最长链即可.场上就写了这个. 100分也没有什么本质区别,没有A串长度 ...
- Codeforces 765F Souvenirs 线段树 + 主席树 (看题解)
Souvenirs 我们将询问离线, 我们从左往右加元素, 如果当前的位置为 i ,用一棵线段树保存区间[x, i]的答案, 每次更新完, 遍历R位于 i 的询问更新答案. 我们先考虑最暴力的做法, ...
- 小结:线段树 & 主席树 & 树状数组
概要: 就是用来维护区间信息,然后各种秀智商游戏. 技巧及注意: 一定要注意标记的下放的顺序及影响!考虑是否有叠加或相互影响的可能! 和平衡树相同,在操作每一个节点时,必须保证祖先的tag已经完全下放 ...
- 洛谷P3834 可持久化线段树(主席树)模板
题目:https://www.luogu.org/problemnew/show/P3834 无法忍受了,我要写主席树! 解决区间第 k 大查询问题,可以用主席树,像前缀和一样建立 n 棵前缀区间的权 ...
随机推荐
- MongoDB学习(1)--安装,基本curd操作
知识点: 1-MongoDB 安装,启动和卸载 2-基本概念 3-基本的增删改查操作(CURD) 来回顾总结一把学习的mongodb,如果有javascript基础,学习"芒果DB" ...
- scala成长之路(1)基本语法和数据类型
scala作为JVM上的Lisp,是一种geek类型的编程语言,也一直是我等java程序员眼中的梦寐以求的一门技能,遂下定决心花一段时间好好学习scala.第一天学习,主要介绍与java在编程上的主要 ...
- jmeter 插件安装
1.下载Plugins Manager插件 打开下载插件地址:https://jmeter-plugins.org/ 2.将下载的plugins-manager.jar包复制到Jmeter安装目录,l ...
- 二维数组快速排序(sort+qsort)
二维数组快速排序 qsort是c中快速排序,如果简单的一维数组排序,想必大家的懂.现在看一下二维数组的排序,虽然可以冒泡但是太费时间了,我们这里使用qsort来快速排序,看代码应该看得懂吧. 代码: ...
- Matplotlib 子图的创建
在matplotlib中,整个图像为一个Figure对象 在Figure对象中可以包含一个或者多个Axes对象 每个Axes对象相当于一个子图了 每个Axes(ax)对象都是一个拥有自己坐标系统的绘 ...
- (数据科学学习手札36)tensorflow实现MLP
一.简介 我们在前面的数据科学学习手札34中也介绍过,作为最典型的神经网络,多层感知机(MLP)结构简单且规则,并且在隐层设计的足够完善时,可以拟合任意连续函数,而除了利用前面介绍的sklearn.n ...
- JS正则表达式笔记
正则表达式(regular expression)描述了一种字符串匹配的模式(pattern),可以用来检查一个串是否含有某种子串.将匹配的子串替换或者从某个串中取出符合某个条件的子串等. 正则 描述 ...
- android去掉button默认的点击阴影
查了资料,发现别人都是说加一个style属性. style="?android:attr/borderlessButtonStyle" 加上了确实管用,但是我绝不是不求甚解的人.追 ...
- HashMap源码注释翻译
HashMap.java(JDK1.8) 如有错误翻译的地方,欢迎评论指出. 介绍:对于HashMap及其子类而言,它们采用Hash算法来决定集合中元素的存储位置.当系统开始初始化HashMap时,系 ...
- 关于实现mybatis order by 排序传递参数实现 问题记录
一 问题场景:本人项目纯纯的后端系统 并且项目前端采用纯纯的原生js 实现 1)表格 通过查询列表数据放入到域中 前段采用 for循环的方式实现遍历生成列表 2)分页实现本人是公司内部自定 ...