[CF1083C]Max Mex


题解

题目就是求树上路径的最大\(Mex\)
直接在树上维护这些东西难度有点大
但是\(Mex\)表示的是最小的没有出现过的自然数
这样我们就可以按照数为下标建立线段树
那么一个代表\([l,r]\)的线段树节点就代表了\([l,r]\)之间的这些数能否构成一条路径
注意:这里的能构成路径不是恰好能形成一条路径,而是不能确定一定不能形成一条路径
那么线段树的每个节点就还需要维护链的两个端点
然后合并信息的时候就是分类讨论
枚举两个端点,看剩下的两个点是否在这条路径上
就大致这么判断

return ((((LCA(x , u) == x) ^ (LCA(x , v) == x)) || LCA(u , v) == x) && (((LCA(y , u) == y) ^ (LCA(y , v) == y)) || LCA(u , v) == y)) ;

然后查询的时候就在线段树上二分
找到最晚的无法构成路径的数即为答案

代码

#include<vector>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
const int M = 400005 ;
using namespace std ;

inline int read() {
    char c = getchar() ; int x = 0 , w = 1 ;
    while(c>'9'||c<'0') { if(c=='-') w = -1 ; c = getchar() ; }
    while(c>='0'&&c<='9') { x = x*10+c-'0' ; c = getchar() ; }
    return x*w ;
}

int n ;
int val[M] , fa[M] , pos[M] , lg[M * 2] ;
int cnt , stp[M] , edp[M] , dep[M] ,  st[M * 2][21] , ans ;
vector < int > vec[M] ;
inline void add_edge(int u , int v) {
    vec[u].push_back(v) ;
}
inline int Mindep(int u , int v) { return dep[u] < dep[v] ? u : v ; }
inline int Maxdep(int u , int v) { return dep[u] > dep[v] ? u : v ; }
void dfs(int u , int father) {
    stp[u] = edp[u] = ++ cnt ; dep[u] = dep[father] + 1 ;  st[cnt][0] = u ;
    for(int i = 0 , v ; i < vec[u].size() ; i ++) {
        v = vec[u][i] ; if(v == father) continue ;
        dfs(v , u) ; st[++cnt][0] = u ; edp[u] = cnt ;
    }
}
inline int LCA(int x , int y) {
    if(dep[x] > dep[y]) swap(x , y) ;
    int l = stp[x] , r = edp[y] ;
    if(l > r) swap(l , r) ;
    int j = lg[r - l + 1] ;
    return Mindep( st[l][j] , st[r - (1 << j) + 1][j] ) ;
}
inline bool On(int u , int v , int x , int y) {
    return ((((LCA(x , u) == x) ^ (LCA(x , v) == x)) || LCA(u , v) == x) && (((LCA(y , u) == y) ^ (LCA(y , v) == y)) || LCA(u , v) == y)) ;
}
# define ls (now << 1)
# define rs (now << 1 | 1)
struct Node {
    bool exist ;
    int lp , rp ;
} t[M * 4] , tmp ;
inline Node Merge(Node p1 , Node p2) {
    Node p ; p.lp = p.rp = -1 ; p.exist = false ;
    int lp1 = p1.lp , rp1 = p1.rp , lp2 = p2.lp , rp2 = p2.rp ;
    if(On(lp1 , rp1 , lp2 , rp2)) p.exist = true , p.lp = lp1 , p.rp = rp1 ;
    else if(On(lp2 , rp2 , lp1 , rp1)) p.exist = true , p.lp = lp2 , p.rp = rp2 ;
    else if(On(lp1 , lp2 , rp1 , rp2)) p.exist = true , p.lp = lp1 , p.rp = lp2 ;
    else if(On(lp1 , rp2 , lp2 , rp1)) p.exist = true , p.lp = lp1 , p.rp = rp2 ;
    else if(On(rp1 , lp2 , lp1 , rp2)) p.exist = true , p.lp = rp1 , p.rp = lp2 ;
    else if(On(rp1 , rp2 , lp1 , lp2)) p.exist = true , p.lp = rp1 , p.rp = rp2 ;
    return p ;
}
void build(int l , int r , int now) {
    if(l == r) {
        t[now].exist = true ;
        t[now].lp = t[now].rp = pos[l] ;
        return ;
    }
    int mid = (l + r) >> 1 ;
    build(l , mid , ls) ;
    build(mid + 1 , r , rs) ;
    t[now] = Merge(t[ls] , t[rs]) ;
}
void Change(int x , int k , int l , int r , int now) {
    if(l == r) {
        t[now].lp = t[now].rp = k ;
        t[now].exist = true ; return ;
    }
    int mid = (l + r) >> 1 ;
    if(mid >= x) Change(x , k , l , mid , ls) ;
    else Change(x , k , mid + 1 , r , rs) ;
    t[now] = Merge(t[ls] , t[rs]) ;
}
void qry(int l , int r , int now) {
    if(l == r) {
        if( tmp.lp == -1 || Merge(tmp , t[now]).exist ) {
            ans = max(ans , l) ;
            if(tmp.lp != -1) tmp = Merge(tmp , t[now]) ;
            else tmp = t[now] ;
        }
        return ;
    }
    int mid = (l + r) >> 1 ;
    if(t[ls].exist && (tmp.lp == -1 || Merge(tmp , t[ls]).exist)) {
        if(tmp.lp != -1) tmp = Merge(tmp , t[ls]) ;
        else tmp = t[ls] ;
        tmp.exist = true ;
        ans = max(ans , mid) ;
        qry(mid + 1 , r , rs) ;
    }
    else qry(l , mid , ls) ;
}
# undef ls
# undef rs

int main() {
    n = read() ;
    for(int i = 2 ; i <= n * 2 ; i ++)
        lg[i] = lg[i >> 1] + 1 ;
    for(int i = 1 ; i <= n ; i ++) {
        val[i] = read() ;
        pos[val[i]] = i ;
    }
    for(int i = 2 ; i <= n ; i ++) {
        fa[i] = read() ;
        add_edge(fa[i] , i) ;
    }
    dfs(1 , 1) ;
    for(int j = 1 ; j <= lg[n * 2] ; j ++)
        for(int i = 1 ; i + (1 << j) - 1 <= n * 2 ; i ++)
            st[i][j] = Mindep( st[i][j - 1] , st[i + (1 << (j - 1))][j - 1] ) ;
    build(0 , n - 1 , 1) ;
    int Q = read() , opt , x , y ;
    while(Q --) {
        opt = read() ;
        if(opt == 1) {
            x = read() ; y = read() ;
            Change(val[x] , pos[val[y]] , 0 , n - 1 , 1) ;
            Change(val[y] , pos[val[x]] , 0 , n - 1 , 1) ;
            swap(pos[val[x]] , pos[val[y]]) ;
            swap(val[x] , val[y]) ;
        }
        else {
            ans = 0 ;
            tmp.lp = tmp.rp = -1 ;
            tmp.exist = true ;
            qry(0 , n - 1 , 1) ;
            printf("%d\n",ans + 1) ;
        }
    }
    return 0 ;
}

原文地址:https://www.cnblogs.com/beretty/p/10726029.html

时间: 2024-10-17 06:56:13

[CF1083C]Max Mex的相关文章

CF1083C Max Mex 线段树

题面 CF1083C Max Mex 题解 首先我们考虑,如果一个数x是某条路径上的mex,那么这个数要满足什么条件? 1 ~ x - 1的数都必须出现过. x必须没出现过. 现在我们要最大化x,那么也就意味着我们要找到一条路径使得这个都出现过的前缀尽可能长. 第二个条件可以忽略,因为如果第1个条件满足,而第2个条件却不满足,意味着我们可以把x至少扩大1位,因为要求最大值,所以扩大肯定最优,因此我们肯定会扩大到不能扩大为止. 由此我们可以发现,x是满足可二分性的. 考虑在线段树上维护这个问题,区

CF1083C Max Mex(线段树上二分)

这题卡倍增害我T了一发= = 显然Mex是可以二分的,于是就可以考虑二分一个Mex然后check一下 然后怎么check呢?可以对点权建一棵线段树,节点\([l,r]\)表示,链上点权的集合包括\([l,r]\)时,最短的链的端点 合并两个区间就是在四个端点间选两个作为新链的端点,判断另外两个端点在不在这条链上,在的话这就是一条合法的链.判断方法就是判断一下两段的距离是否等于一整条链的距离. 这样时间复杂度是\(O(nlog^2n)\),感觉可过的样子?然而还可以在线段树上二分把时间复杂度优化到

Max Mex

Max Mex 无法直接处理 可以二分答案! [0,mid]是否在同一个链上? 可以不修改地做了 修改? 能不能信息合并?可以! 记录包含[l,r]的最短链的两端 可以[0,k][k+1,mid]合并:枚举四个端点中的两个,使得另外两个一定在这两个的路径上 (判断z点在x,y路径上:(lca(x,z)==z||lca(y,z)=z)&&(lca(lca(x,y),z)=lca(x,y))画图即可理解 能合并,所以线段树可以维护. 线段树维护 线段树上二分. LCA用ST表存 #includ

BZOJ 4144: [AMPPZ2014]Petrol

4144: [AMPPZ2014]Petrol Time Limit: 10 Sec  Memory Limit: 256 MBSubmit: 457  Solved: 170[Submit][Status][Discuss] Description 给定一个n个点.m条边的带权无向图,其中有s个点是加油站. 每辆车都有一个油量上限b,即每次行走距离不能超过b,但在加油站可以补满. q次询问,每次给出x,y,b,表示出发点是x,终点是y,油量上限为b,且保证x点和y点都是加油站,请回答能否从x走

HDU #4747 MEX

题目描述: 定义 mex(i, j) 为序列中第 i 项到第 j 项中没有出现的最小自然数.给定序列,求 Σ1≤i,j≤n,i≤j mex(i, j). 解题思路: 首先我们可以 O(n) 预处理出 mex(1, 1 ~ n),因为显然的是mex是递增的.然后我们考虑怎么从 mex(i, i ~ n) 推出 mex(i + 1, i + 1 ~ n),我们删掉 a[i] 这个数后,哪些区间的mex会改变呢?其实就是到下一个a[i]出现前mex大于a[i]的区间,因为这段区间没有了a[i]这个数,

[HDU 4747] Mex (线段树)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4747 这道题是我去年刚入校队的时候参加网赛的题目. 一年过去了,我依然还是不会做.. 这是我难题计划的开始吧.. 竟然还敲挫了,自己真是弱. 题意: 给你一个数列a,定义Mex[L,R]为a[L,R]中没有出现过的最小的自然数.求1<=l<=r<=n的所有Mex[l,r]之和. 解法我也是看了解题报告才知道的. 请参看cxlove大神的博文:http://blog.csdn.net/acm_

Matlab中利用mex编译Opencv实现画板绘图功能

图形绘制是标记和可视化数据的重要方法. 通过在Matlab中集成画板绘图功能, 可为科学计算提供便利. 1 设置Matlab支持Opencv编译 操作系统: 麒麟14.04(基于Ubuntu 14.04) 命令: mex -v -> mexopts.sh sourced from directory (DIR = $MATLAB/bin) FILE = /usr/local/MATLAB/R2013a/bin/mexopts.sh --------------------------------

Mex(线段树的巧妙应用)

题目要求求某段区间第一个没有出现的数(0,1,2,3....) ,对于所有的区间,我们把这样的数加起来最后得到一个结果. 首先,我们要求出这样的数,然后还得列举出所有的区间,复杂度太大了. 换种思路,我们定住L,是不是一次性能求出所有的R所得出的结果,这就用到线段树的性质了,因为在移动L的过程中,移一步只变化一个数,那么就可以用线段树进行维护. 首先求出[1,R] 以1为左端的所有区间的情况,记录每个点也就是1到那个点的这段区间值sum[i],以这个值建一颗树,那么在L向前移动的时候,每次丢掉一

B - Alyona and Mex

Description Someone gave Alyona an array containing n positive integers a1, a2, ..., an. In one operation, Alyona can choose any element of the array and decrease it, i.e. replace with any positive integer that is smaller than the current one. Alyona