题意
给定一棵树,点权为 \(0\sim n-1\) 的排列。每次交换两个点的点权或者询问整棵树上所有路径中,路径上点权的 \(\operatorname{mex}\) 最大值。
Solution
比较神奇的转化。
考虑放宽题目中的限制,我们在区间 \([l,r]\) 维护树上路径中包含 \([l,r]\) 的数的最短路径的两个端点,显然这是不一定存在的。那么对于询问来说,我就是要找到最大的 \(r\) 使得 \([0,r]\) 上的答案存在。
为什么这样转化?这样转化之后,这个信息就具有合并性,可以直接用线段树维护了。
也就是说,我们在线段树的每个节点上维护当前区间内都出现了的最短路径端点。合并的时候,得到两段子区间答案的路径端点,然后在这 \(4\) 个端点中选两个作为新的路径端点,然后只要判断剩下两个是否在该路径上就可以了。
为了判断是否在当前路径上,可以采取一个套路:判断一个点到路径两端的距离和是否等于路径长度。路径长度可以用 LCA 加上深度来做。
最后为了找到最大的 \(r\),需要一个二分。复杂度两只老哥。线段树上二分加上 \(O(1)\) LCA 可以做到一只老哥,可是没必要。
线段树的应用都是在信息具有可合并性的时候。如果信息没有一定的可合并性,那么没有条件创造条件也要上。
Code
// Problem:
// Max Mex
//
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/CF1083C
// Memory Limit: 2900 MB
// Time Limit: 250000 ms
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb emplace_back
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define se second
#define all(a) a.begin(),a.end()
#define siz(a) (int)a.size()
#define clr(a) memset(a,0,sizeof(a))
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
#define pt(a) cerr<<#a<<'='<<a<<' '
#define pts(a) cerr<<#a<<'='<<a<<'\n'
// #define int long long
using namespace std;
const int MAXN=2e5+10;
vector<int> e[MAXN];
int fa[MAXN],dep[MAXN],siz[MAXN],son[MAXN];
void dfs1(int x){
siz[x]=1;son[x]=-1;
for(int s:e[x]){
fa[s]=x;dep[s]=dep[x]+1;
dfs1(s);siz[x]+=siz[s];
if(son[x]==-1||siz[son[x]]<siz[s])
son[x]=s;
}
}
int top[MAXN];
void dfs2(int x,int t){
top[x]=t;if(~son[x]) dfs2(son[x],t);
for(int s:e[x])
if(s!=son[x])
dfs2(s,s);
}
int LCA(int u,int v){
while(top[u]^top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
u=fa[top[u]];
}return dep[u]>dep[v]?v:u;
}
int dis(int u,int v){
return dep[u]+dep[v]-2*dep[LCA(u,v)];
}
bool onp(int u,int v,int x){
return dis(u,v)==dis(u,x)+dis(x,v);
}
int p[MAXN],a[MAXN];
struct info{
int u,v;
info(){}
info(int p){u=v=p;}
info friend operator+(info a,info b){
info ret;ret.u=ret.v=-1;
if(a.u==-1||b.u==-1) return ret;
if(onp(a.u,a.v,b.u)&&onp(a.u,a.v,b.v)) ret.u=a.u,ret.v=a.v;
if(onp(a.u,b.u,a.v)&&onp(a.u,b.u,b.v)) ret.u=a.u,ret.v=b.u;
if(onp(a.u,b.v,b.u)&&onp(a.u,b.v,a.v)) ret.u=a.u,ret.v=b.v;
if(onp(a.v,b.u,b.v)&&onp(a.v,b.u,a.u)) ret.u=a.v,ret.v=b.u;
if(onp(a.v,b.v,a.u)&&onp(a.v,b.v,b.u)) ret.u=a.v,ret.v=b.v;
if(onp(b.u,b.v,a.u)&&onp(b.u,b.v,a.v)) ret.u=b.u,ret.v=b.v;
return ret;
}
};
struct Tree{int l,r;info mex;}tr[MAXN<<2];
#define ls i<<1
#define rs i<<1|1
void pushup(int i){tr[i].mex=tr[ls].mex+tr[rs].mex;}
void build(int i,int l,int r){
tr[i].l=l;tr[i].r=r;if(l==r){tr[i].mex=info(a[l]);return;}
int mid=(l+r)>>1;build(ls,l,mid);build(rs,mid+1,r);pushup(i);
}
void upd(int i,int x,int v){
if(tr[i].l==tr[i].r){tr[i].mex=info(v);return;}int mid=(tr[i].l+tr[i].r)>>1;
if(x<=mid) upd(ls,x,v);else upd(rs,x,v);pushup(i);
}
info ask(int i,int l,int r){
if(tr[i].l==l&&tr[i].r==r) return tr[i].mex;int mid=(tr[i].l+tr[i].r)>>1;
if(r<=mid) return ask(ls,l,r);else if(l>mid) return ask(rs,l,r);
else return ask(ls,l,mid)+ask(rs,mid+1,r);
}
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
int n;cin>>n;
rep(i,1,n) cin>>p[i],a[p[i]]=i;a[n]=-1;
rep(i,2,n) cin>>fa[i],e[fa[i]].pb(i);
dfs1(1);dfs2(1,1);build(1,0,n);
int Q,op,x,y;cin>>Q;
while(Q--){
cin>>op;
if(op==1){
cin>>x>>y;
upd(1,p[x],y);upd(1,p[y],x);
swap(p[x],p[y]);
}else{
int l=1,r=n,ans=1;
while(l<=r){
int mid=(l+r)>>1;
if(ask(1,0,mid).u!=-1) ans=mid,l=mid+1;
else r=mid-1;
}cout<<ans+1<<'\n';
}
}
return 0;
}
标签:info,int,CF1083C,mid,ret,Max,onp,Mex,define
From: https://www.cnblogs.com/ZCETHAN/p/16892956.html