CF1083C Max Mex 线段树
题面
题解
首先我们考虑,如果一个数x是某条路径上的mex,那么这个数要满足什么条件?
- 1 ~ x - 1的数都必须出现过.
- x必须没出现过。
现在我们要最大化x,那么也就意味着我们要找到一条路径使得这个都出现过的前缀尽可能长。
第二个条件可以忽略,因为如果第1个条件满足,而第2个条件却不满足,意味着我们可以把x至少扩大1位,因为要求最大值,所以扩大肯定最优,因此我们肯定会扩大到不能扩大为止。
由此我们可以发现,x是满足可二分性的。
考虑在线段树上维护这个问题,区间\([l, r]\)表示值域区间为\([l, r]\)的点全都连接在一起形成的一条路径。
如果有这条路径,那么就存下左右端点,否则就不存。
那么考虑如何合并2个区间\([l, mid], [mid + 1, r]\)。
从4个端点中任取2个作为新的端点,如果另外2个端点均在这2个端点构成的路径上,那么我们就找到了一条新路径满足值域属于\([l, r]\)的点全在路径上。
这样做的正确性(最优性)依赖于权值两两不同,因此我们的选择是唯一的,因此肯定是最优选择。
有一个很简洁的式子可以用于判断一个点是否在某条路径上:
如果\(x\)在链\((u, v)\)上,设\(LCA(u, v) = y\)那么有:
\]
于是维护好之后剩下的事情就是在线段树上查询(二分)了。
#include<bits/stdc++.h>
using namespace std;
#define R register int
#define LL long long
#define AC 201000
#define ac 802000
#define h(x) ((x) << 1)
int n, m;
int Head[AC], date[AC], Next[AC], tot;
int v[AC], fa[AC], s[AC];//s[i]表示权值为i的是哪个,只需要在最开始用就可以了
inline int read()
{
int x = 0;char c = getchar();
while(c > '9' || c < '0') c = getchar();
while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
return x;
}
inline void add(int f, int w)
{date[++ tot] = w, Next[tot] = Head[f], Head[f] = tot;}
#define cal(x, y) ((dep[x] < dep[y]) ? x : y)//返回x, y中dep最小的那个
#define maxn 401000
int st[maxn][20], ss[maxn], dep[AC], t[maxn], p[maxn], first[AC], top;
struct ST_form{
void dfs(int x)//求遍历的数组 + dep
{
ss[++ top] = x, first[x] = top;
for(R i = Head[x]; i; i = Next[i])
{
int now = date[i];
dep[now] = dep[x] + 1, dfs(now), ss[++ top] = x;//返回的时候也要加
}
}
void build()
{
dep[1] = 1, dfs(1);
int tmp = 1, cnt = 0;
for(R i = 1; i <= top; i ++)
{
if(i == (tmp << 1)) tmp <<= 1, ++ cnt;
p[i] = tmp, t[i] = cnt;
}
for(R i = 1; i <= top; i ++) st[i][0] = ss[i];
tmp = 1;
for(R i = 1; i <= 18; i ++)
{
for(R j = 1; j <= top; j ++)
st[j][i] = cal(st[j][i - 1], st[min(j + tmp, top)][i - 1]);
tmp <<= 1;
}
}
inline int LCA(int x, int y)//st表求LCA
{
int l = first[x], r = first[y];
if(l > r) swap(l, r);//不一定按顺序的
int len = r - l + 1;
return cal(st[l][t[len]], st[r - p[len] + 1][t[len]]);
}
}T;
struct node{
int lx, rx;
}tree[ac], go;
#define update(x) tree[x] = merge(tree[x << 1], tree[(x << 1) + 1]);
struct seg_tree{
bool check(int x, int u, int v)//检查x是否在路径(u, v)上
{
int y = T.LCA(u, v);
return ((T.LCA(u, x) == x || T.LCA(v, x) == x) && T.LCA(x, y) == y);
}
inline node merge(node ll, node rr)
{
if(!ll.lx || !rr.lx) return (node){0, 0};
int s[5] = {ll.lx, ll.rx, rr.lx, rr.rx};
for(R i = 0; i < 4; i ++)
for(R j = i + 1; j < 4; j ++)
{
bool flag = true;
if(s[i] == s[j]) continue;
for(R k = 0; k < 4; k ++)
if(k != i && k != j && !check(s[k], s[i], s[j])) {flag = false; break;}
if(flag) return (node){s[i], s[j]};
}
return (node){0, 0};
}
void build(int x, int l, int r)
{
if(l == r) {tree[x].lx = tree[x].rx = s[l]; return ;}
int mid = (l + r) >> 1;
build(h(x), l, mid), build(h(x) + 1, mid + 1, r);
update(x);
}
void change(int x, int l, int r, int w)
{
if(l == r) {tree[x].lx = tree[x].rx = w; return ;}
int mid = (l + r) >> 1;
if(v[w] <= mid) change(h(x), l, mid, w);
else change(h(x) + 1, mid + 1, r, w);
update(x);
}
int find(int x, int l, int r)
{
int mid = (l + r) >> 1;
if(l == r)
{
node tmp = go.lx ? merge(go, tree[x]) : tree[x];
if(tmp.lx) {go = tmp; return 1;}
return 0;
}
if(!tree[h(x)].lx) return find(h(x), l, mid);
else
{
node tmp = go.lx ? merge(go, tree[h(x)]) : tree[h(x)];
if(!tmp.lx) return find(h(x), l, mid);
else {go = tmp; return mid - l + 1 + find(h(x) + 1, mid + 1, r);}
}
}
}T1;
void pre()
{
n = read();
for(R i = 1; i <= n; i ++) v[i] = read() + 1, s[v[i]] = i;
for(R i = 2; i <= n; i ++) fa[i] = read(), add(fa[i], i);
}
void work()
{
m = read();
for(R i = 1; i <= m; i ++)
{
int opt = read();
if(opt == 1)
{
int x = read(), y = read();
swap(v[x], v[y]);
T1.change(1, 1, n, x), T1.change(1, 1, n, y);
}
else go.lx = go.rx = 0, printf("%d\n", T1.find(1, 1, n));//因为向右平移了1,所以要移回来,+ 1 - 1就恰好抵消了
}
}
int main()
{
// freopen("in.in", "r", stdin);
pre();
T.build();
T1.build(1, 1, n);
work();
// fclose(stdin);
return 0;
}
CF1083C Max Mex 线段树的更多相关文章
- Codeforces 1083C Max Mex [线段树]
洛谷 Codeforces 思路 很容易发现答案满足单调性,可以二分答案. 接下来询问就转换成判断前缀点集是否能组成一条链. 我最初的想法:找到点集的直径,判断直径是否覆盖了所有点,需要用到树套树,复 ...
- HDU-4747 Mex 线段树
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4747 题意:求一个数列中,所有mex(L,R)的和. 注意到mex是单调不降的,那么首先预处理出mex ...
- [置顶] hdu4747 Mex 线段树
题意:给你一个序列,让你求出对于所有区间<i, j>的mex和,mex表示该区间没有出现过的最小的整数. 思路:从时限和点数就可以看出是线段树,并且我们可以枚举左端点i, 然后求出所有左端 ...
- hdu 4747 mex 线段树+思维
http://acm.hdu.edu.cn/showproblem.php?pid=4747 题意: 我们定义mex(l,r)表示一个序列a[l]....a[r]中没有出现过得最小的非负整数, 然后我 ...
- [CF1083C]Max Mex
题目大意:有一棵$n(n\leqslant2\times10^5)$个点的树,每个点有点权,所有的点权构成了$0\sim n-1$的排列.$q(q\leqslant2\times10^5)$次操作,操 ...
- BZOJ.3585.mex(线段树)
题目链接 题意:多次求区间\(mex\). 考虑\([1,i]\)的\(mex[i]\),显然是单调的 而对于\([l,r]\)与\([l+1,r]\),如果\(nxt[a[l]]>r\),那么 ...
- bzoj 3585 mex - 线段树 - 分块 - 莫队算法
Description 有一个长度为n的数组{a1,a2,...,an}.m次询问,每次询问一个区间内最小没有出现过的自然数. Input 第一行n,m. 第二行为n个数. 从第三行开始,每行一个询问 ...
- 【bzoj3585】mex 线段树 mex,sg
Description 有一个长度为n的数组{a1,a2,…,an}.m次询问,每次询问一个区间内最小没有出现过的自然数. Input 第一行n,m. 第二行为n个数. 从第三行开始,每行一个询问l, ...
- hdu 4747 Mex( 线段树? 不,区间处理就行(dp?))
Mex Time Limit: 15000/5000 MS (Java/Others) Memory Limit: 65535/65535 K (Java/Others)Total Submis ...
随机推荐
- Linux学习之常用系统工作命令(一)
由于centos和RHEL互通,两个版本可以相互学习,所以截图有两个界面 Linux系统与win系列是两个几乎完全不同的操作系统,但是就应用范围来说,是win系统更胜一筹,然而,这反而也成为win系 ...
- python魔法方法大全
1.python魔法方法详解: python魔法方法是可以修改重载的,如果你的对象实现(重载)了这些方法中的某一个,那么这个方法就会在特殊的情况下被 Python 所调用,你可以定义自己想要的行为,而 ...
- 实现activity跳转动画的若干种方式
第一种: (使用overridePendingTransition方法实现Activity跳转动画) 在Activity中代码如下 /** * 点击按钮实现跳转逻辑 */ button1.setOnC ...
- 《杜增强讲Unity之Tanks坦克大战》8-子弹碰撞处理
8 子弹碰撞处理 为了处理子弹打到坦克的伤害我们在这里新建一个Shell.cs 子弹有两种情况,碰到坦克炸开,没有碰到坦克则过2s子弹销毁. void Start () { Destroy (game ...
- 《Redis设计与实现》阅读笔记(三)--链表
链表 定义 链表分为两部分,链表节点和持有链表的list结构. 每个链表节点包含前置节点指针,后置节点指针,节点值void*用于保存各种不同类型的值 list结构包含表头节点指针,表尾节点指针,节点数 ...
- 如何在unix系统中用别的用户运行一个程序?
1.问题的缘由 实际开发系统的时候,经常需要用别的用户运行一个程序.比如,有些系统为保证系统安全,不允许使用root来运行.这里,我们总结了unix系统下如何解决这个问题的一些方法.同时,我们还讨论如 ...
- ubuntu 相关软件设置
软件篇 1. 网易云音乐软件 首先去官网下载网易云音乐客户端linux版,网址:http://music.163.com/#/download,选择linux版本,然后选择ubuntu 16.04(6 ...
- Hands on Machine Learning with Sklearn and TensorFlow学习笔记——机器学习概览
一.什么是机器学习? 计算机程序利用经验E(训练数据)学习任务T(要做什么,即目标),性能是P(性能指标),如果针对任务T的性能P随着经验E不断增长,成为机器学习.[这是汤姆米切尔在1997年定义] ...
- import 导入包的特别用法总结
指定别名 可以为包指定一个别名,以便记忆或提高输入效率 如 import str "strings" 在使用的时候可以直接使用别名,如原先要写成strings.Contains,现 ...
- 记一次线上gc调优的过程
近期公司运营同学经常表示线上我们一个后台管理系统运行特别慢,而且经常出现504超时的情况.对于这种情况我们本能的认为可能是代码有性能问题,可能有死循环或者是数据库调用次数过多导致接口运行 ...