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表存

#include<bits/stdc++.h>
#define reg register int
#define il inline
#define fi first
#define se second
#define mk(a,b) make_pair(a,b)
#define numb (ch^'0')
using namespace std;
typedef long long ll;
template<class T>il void rd(T &x){
    char ch;x=0;bool fl=false;
    while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);
    (fl==true)&&(x=-x);
}
template<class T>il void output(T x){if(x/10)output(x/10);putchar(x%10+'0');}
template<class T>il void ot(T x){if(x<0) putchar('-'),x=-x;output(x);putchar(' ');}
template<class T>il void prt(T a[],int st,int nd){for(reg i=st;i<=nd;++i) ot(a[i]);putchar('\n');}

namespace Miracle{
const int N=2e5+5;
int n,q;
struct node{
    int nxt,to;
}e[2*N];
int hd[N],cnt;
void add(int x,int y){
    e[++cnt].nxt=hd[x];
    e[cnt].to=y;
    hd[x]=cnt;
}
int dep[N];
int a[2*N],tot;
int dfn[N];
int f[2*N][20];
int lg[2*N];
int id[N],fid[N];
void dfs(int x,int d){
    dep[x]=d;
    a[++tot]=x;
    dfn[x]=tot;
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        dfs(y,d+1);
        a[++tot]=x;
    }
}
int big(int x,int y){
    return dep[x]<dep[y]?x:y;
}
int lca(int x,int y){
    if(dfn[x]>dfn[y]) swap(x,y);
    int len=lg[dfn[y]-dfn[x]+1];
    return big(f[dfn[x]][len],f[dfn[y]-(1<<len)+1][len]);
}
int on(int p1,int p2,int p3){
    int y=lca(p1,p2);
//    cout<<" p1 "<<p1<<" p2 "<<p2<<" p3 "<<p3<<" y "<<y<<endl;
    return ((lca(p1,p3)==p3||lca(p2,p3)==p3)&&(lca(y,p3)==y));
}
struct tr{
    int p[2];
    int can;
    void init(){
        can=p[0]=p[1]=0;
    }
    void op(){
        cout<<" can "<<can<<" p[0] "<<p[0]<<" p[1] "<<p[1]<<endl;
    }
}t[4*N];
#define ls (x<<1)
#define rs (x<<1|1)
#define mid ((l+r)>>1)
void pushup(int x){
    t[x].init();
    if(t[x<<1].can&&t[x<<1|1].can){
        t[x].can|=on(t[ls].p[0],t[ls].p[1],t[rs].p[0])&&on(t[ls].p[0],t[ls].p[1],t[rs].p[1]);
        if(t[x].can==1) {t[x].p[0]=t[ls].p[0],t[x].p[1]=t[ls].p[1];return;}
        t[x].can|=on(t[rs].p[0],t[rs].p[1],t[ls].p[0])&&on(t[rs].p[0],t[rs].p[1],t[ls].p[1]);
        if(t[x].can==1) {t[x].p[0]=t[rs].p[0],t[x].p[1]=t[rs].p[1];return;}
        if(!t[x].can){
            for(reg i=0;i<=1;++i){
                for(reg j=0;j<=1;++j){
                    t[x].can|=on(t[ls].p[i],t[rs].p[j],t[ls].p[i^1])&&on(t[ls].p[i],t[rs].p[j],t[rs].p[j^1]);
                    if(t[x].can==1) {t[x].p[0]=t[ls].p[i],t[x].p[1]=t[rs].p[j];return;}
                }
            }
        }
    }
}
tr merge(tr A,tr B){
    tr C;C.can=C.p[0]=C.p[1]=0;
//    cout<<" A ";A.op();
//    cout<<" B ";B.op();
    if(A.can&&B.can){
        C.can|=on(A.p[0],A.p[1],B.p[0])&&on(A.p[0],A.p[1],B.p[1]);
        if(C.can==1) {C.p[0]=A.p[0],C.p[1]=A.p[1];return C;}
        C.can|=on(B.p[0],B.p[1],A.p[0])&&on(B.p[0],B.p[1],A.p[1]);
        if(C.can==1) {C.p[0]=B.p[0],C.p[1]=B.p[1];return C;}
        if(!C.can){
            for(reg i=0;i<=1;++i){
                for(reg j=0;j<=1;++j){
                    C.can|=on(A.p[i],B.p[j],A.p[i^1])&&on(A.p[i],B.p[j],B.p[j^1]);
                    if(C.can==1) {C.p[0]=A.p[i],C.p[1]=B.p[j];return C;}
                }
            }
        }
    }
    return C;
}
void build(int x,int l,int r){
    if(l==r){
        t[x].p[0]=t[x].p[1]=fid[l];
        t[x].can=1;return;
    }
    t[x].can=0;
    build(x<<1,l,mid);
    build(x<<1|1,mid+1,r);
    pushup(x);
}
void upda(int x,int l,int r,int p,int c){//pos to c
    if(l==r){
        t[x].p[0]=c;t[x].p[1]=c;
        t[x].can=1;return;
    }
    if(p<=mid) upda(ls,l,mid,p,c);
    else upda(rs,mid+1,r,p,c);
    pushup(x);
}
tr tmp;
int query(int x,int l,int r){
//    cout<<" query "<<x<<" "<<l<<" "<<r<<" : ";t[x].op();
    
    tr C;
    if(l==r){
        if(tmp.can==-1){
            C=t[x];
        }else{
            C=merge(tmp,t[x]);
        }
        return C.can==1?l:l-1;
    }
//    cout<<" ls ";t[ls].op();
    if(tmp.can==-1){
        C=t[ls];
    }else{
        C=merge(tmp,t[ls]);
    }
//    C.op();
    if(C.can){
        tmp=C;
        return query(rs,mid+1,r);
    }else{
        return query(ls,l,mid);
    }
}
int main(){
    rd(n);
    for(reg i=1;i<=n;++i){
        rd(id[i]);fid[id[i]]=i;
    }
    int y;
    for(reg i=2;i<=n;++i){
        rd(y);add(y,i);
    }
    dfs(1,1);
    for(reg i=1;i<=2*n-1;++i){
        lg[i]=(i>>(lg[i-1]+1))?lg[i-1]+1:lg[i-1];
        f[i][0]=a[i];
    }
//    cout<<" tot "<<tot<<endl;
//    prt(a,1,tot);
//    prt(lg,1,2*n-1);
    for(reg j=1;j<=19;++j){
        for(reg i=1;i+(1<<j)-1<=tot;++i){
            f[i][j]=big(f[i][j-1],f[i+(1<<(j-1))][j-1]);
        }
    }
    build(1,0,n-1);
    
    rd(q);
    int op,x;
    while(q--){
        rd(op);
        if(op==1){
            rd(x);rd(y);
            upda(1,0,n-1,id[x],y);
            upda(1,0,n-1,id[y],x);
            swap(id[x],id[y]);
        }else{
            tmp.init();
            tmp.can=-1;
            printf("%d\n",query(1,0,n-1)+1);
        }
    }
    return 0;
}

}
signed main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
   Date: 2019/4/11 16:56:19
*/

正难则反考虑二分检验。

[IOI2018] seats 排座位有点像,维护前缀信息的连通性

修改?能否两两区间合并?树上链的信息合并的套路。

 

posted @ 2019-04-11 18:56  *Miracle*  阅读(226)  评论(0编辑  收藏  举报