题目
https://codeforces.com/contest/1084/problem/F
题意
给一棵树t,t上结点用[0,n)的排列p[]标号。
有两种询问:
1.交换结点i和结点j的标号。
2.求t上一条路径,包括[0,k]的所有数,要求k尽量大,输出这个最大的k。
q
<
2
e
5
,
n
<
2
e
5
q<2e5,n<2e5
q<2e5,n<2e5
思路
大致思路就是用线段树维护标号为[l,r]的结点是不是在一条路径上。
首先需要维护lca:
这里用dfs序+st表维护原始树的lca(讯问中交换两个结点的序号,可以映射回原来的树的结点),因为线段树合并的时候需要多次询问lca,为了让复杂度少个log。
线段树维护:
[l,r]的标号是否在一条路径上,[l,r]的标号所在的路径的左端点,右端点。
合并两条路径:
先拿出左右儿子路径的4个点(也有可能是3个)中距离最远的两个点(这一部分用lca就可以求出),然后考虑所有点是不是都在这两个点的路径上。如果在,说明可以合并,否则不能。
询问的时候:
思路就是贪心,先考虑左边一段能不能合并到总的一段,如果可以就query右边一段,否则就query左边一段,到线段树叶子结点时,考虑是否能并于总的一段即可。我一开始写二分(md二分根本t爆的…),其实O(log)就可以询问。
注意st表,标号,dfn序的映射,映射我写的也太差了。
总复杂度
O
(
(
n
+
q
)
l
o
g
n
∗
16
∗
k
)
k
O((n+q)logn*16*k)k
O((n+q)logn∗16∗k)k是线段树常数,16是找出2条路径端点中最远的两个端点。
这题写一天…差不多用尽毕生所学…2916ms/3000ms(自己写的常数很大)
int ju(int a,int b,int c){ //判断c是不是在a->b的路径上。
int lca=LCA(a,b);
if(LCA(c,lca)==lca&&(LCA(c,b)==c||LCA(c,a)==c)) return 1;
return 0;
}
/* Author : Rshs */
#include<bits/stdc++.h>
using namespace std;
#define FI first
#define SE second
#define LL long long
#define LDB long double
#define MP make_pair
#define PII pair<int,int>
#define SZ(a) (int)a.size()
const LDB pai = acos(-1.0L);
const LDB eps = 1e-10;
const LL mod = 1e9+7;
const int MXN = 2e5+5;
const int logMXN = 25;
int p[MXN],n,fa[MXN];
vector<int>g[MXN];
int dfs_clock=0;
int dfn[MXN*2],de[MXN],fst[MXN],id[MXN];
int st[MXN*2][logMXN],lgg[MXN*2];
int L[MXN*4],R[MXN*4],T[MXN*4];
void dfs(int now,int pre){
dfn[++dfs_clock]=now;
fst[now]=dfs_clock;
for(auto i:g[now]){
if(i==pre) continue;
de[i]=de[now]+1;
dfs(i,now);
dfn[++dfs_clock]=now;
}
}
void ST_build(int nn){
for(int i=1;i<=nn;i++) st[i][0]=i;
for(int j=1;(1<<j)<=nn;j++){
for(int i=1;i+(1<<j)-1<=nn;i++){
if(de[dfn[st[i][j-1]]]<de[dfn[st[i+(1<<(j-1))][j-1]]]) st[i][j]=st[i][j-1];
else st[i][j]=st[i+(1<<(j-1))][j-1];
}
}
for(int i=1;i<=nn;i++) lgg[i]=(int)(log2((double)(i)));
}
int LCA(int tl,int tr){
int l=fst[id[tl]],r=fst[id[tr]];
if(l>r)swap(l,r);
int lloo=lgg[r-l+1];
if(de[dfn[st[l][lloo]]]<de[dfn[st[r-(1<<lloo)+1][lloo]]]) return p[dfn[st[l][lloo]]];
return p[dfn[st[r-(1<<lloo)+1][lloo]]];
}
int ju(int a,int b,int c){ //cÔÚa->b ÉÏ
int lca=LCA(a,b);
if(LCA(c,lca)==lca&&(LCA(c,b)==c||LCA(c,a)==c)) return 1;
return 0;
}
pair<int,pair<int,int>> ck(int a,int b,int c,int d){
if(c==-1)return MP(1,MP(a,b));
vector<int>v;
v.push_back(a);v.push_back(b);
v.push_back(c);v.push_back(d);
sort(v.begin(),v.end());
v.erase(unique(v.begin(),v.end()),v.end());
int tl,tr,di=-1;
for(auto i:v){
for(auto j:v){
int lca=LCA(i,j);
int zz=-de[id[lca]]*2+de[id[i]]+de[id[j]];
if(di<zz) di=zz,tl=i,tr=j;
}
}
for(int i=0;i<SZ(v);i++) if(ju(tl,tr,v[i])==0){return MP(0,MP(0,0));}
return MP(1,MP(tl,tr));
}
void update(int l,int r,int rt,int pos){
if(l==r){
T[rt]=1;
L[rt]=l,R[rt]=r;
return ;
}
int m=(l+r)/2;
if(pos<=m) update(l,m,rt<<1,pos);
if(pos>m) update(m+1,r,rt<<1|1,pos);
if(T[rt<<1]==0||T[rt<<1|1]==0) {
T[rt]=0;return ;
}
auto f=ck(L[rt<<1],R[rt<<1],L[rt<<1|1],R[rt<<1|1]);
if(f.FI) {
T[rt]=1;
L[rt]=f.SE.FI,R[rt]=f.SE.SE;
return ;
}
T[rt]=0;
}
int LLL=-1,RRR=-1;
int query(int l,int r,int rt){
if(l==r){
auto f=ck(L[rt],R[rt],LLL,RRR);
if(f.FI) {
LLL=f.SE.FI;
RRR=f.SE.SE;
return 1;
}
else return 0;
}
int mid=(l+r)/2;
int ls=rt<<1,rs=rt<<1|1;
auto f=ck(L[ls],R[ls],LLL,RRR);
if(T[ls]==0) return query(l,mid,ls);
if(T[ls]==1&&f.FI) {
LLL=f.SE.FI;
RRR=f.SE.SE;
return (mid-l+1)+query(mid+1,r,rs);
}
else {
return query(l,mid,ls);
}
}
int main(){
cin>>n;
for(int i=1;i<=n;i++)scanf("%d",&p[i]);
for(int i=1;i<=n;i++) id[p[i]]=i;
for(int i=2;i<=n;i++) {
scanf("%d",&fa[i]);
g[fa[i]].push_back(i);g[i].push_back(fa[i]);
}
de[1]=1;
dfs(1,-1);
ST_build(dfs_clock);
//cout<<LCA(2,4)<<'\n';
for(int i=0;i<n;i++)
update(0,n-1,1,i);
int q;cin>>q;
while(q--){
int op;scanf("%d",&op);
if(op==2) {
LLL =-1,RRR=-1;
cout<<query(0,n-1,1)<<'\n';
}
if(op==1){
int sa,sb;scanf("%d%d",&sa,&sb);
swap(id[p[sa]],id[p[sb]]);
swap(p[sa],p[sb]);
update(0,n-1,1,p[sa]);
update(0,n-1,1,p[sb]);
}
}
return 0;
}