题目
有个排列 p p p。
对于两个位置 i i i和 j j j,如果有边从 i i i连向 j j j,当且仅当 p i > p j p_i>p_j pi>pj且对于任意 k ∈ ( min ( i , j ) , max ( i , j ) ) k\in (\min(i,j),\max(i,j)) k∈(min(i,j),max(i,j)), p k < p j p_k<p_j pk<pj。
你要选择一些点,选择某个点之后这个点以及其连向的点会被覆盖。
求要覆盖所有点最少需要的点。
支持交换相邻两个位置的操作。
n ≤ 1 0 5 n\le 10^5 n≤105
q ≤ 2 ∗ 1 0 4 q\le 2*10^4 q≤2∗104
数据随机。
转化一下题意:连边的方式相当于,对序列建一棵笛卡尔树,一个点连向的它左子树的右链和右子树的左链。
先不考虑修改,先考虑一个询问改怎么求:
DP:设 f i , 0 / 1 , 0 / 1 , 0 / 1 f_{i,0/1,0/1,0/1} fi,0/1,0/1,0/1表示 i i i的子树中, i i i自己是否被覆盖, i i i的左链是否被覆盖, i i i的右链是否被覆盖,此时的最小代价。
接下来要支持修改,那么可以维护这棵笛卡尔树。由于数据随机,所以树高为 O ( lg n ) O(\lg n) O(lgn)级别。
题解是根据交换相邻两个位置的特殊性来考虑,我是直接改节点的权值然后旋转(这样可以稍微扩展一下题目,变成交换任意两个)。
于是总时间复杂度为 O ( 8 q lg n ) O(8q\lg n) O(8qlgn)
using namespace std;
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cassert>
#define N 100010
int n,Q;
int p[N];
struct Node *null,*rt;
struct Node{
Node *fa,*c[2];
int v;
int f[2][2][2];
bool getson(){return fa->c[0]!=this;}
void upd(){
for (int k=0;k<2;++k)
for (int i=0;i<2;++i)
for (int j=0;j<2;++j){
f[k][i][j]=c[0]->f[0][i][0]+c[1]->f[0][0][j]+1;
if (!k)
f[k][i][j]=min(f[k][i][j],c[0]->f[i][i][1]+c[1]->f[j][1][j]);
}
}
int ans(){return f[1][1][1];}
void rotate(){
Node *y=fa,*z=y->fa;
if (z==null)
rt=this;
else
z->c[y->getson()]=this;
int k=getson();
fa=z;
y->c[k]=c[!k],c[!k]->fa=y;
c[!k]=y,y->fa=this;
y->upd(),upd();
}
} d[N];
Node *build(){
null=d;
*null={null,null,null,0};
static int st[N];
int tp=0;
for (int i=1;i<=n;++i){
d[i].v=p[i];
Node *lst=null;
while (tp && p[st[tp]]<p[i]){
lst=&d[st[tp]];
--tp;
}
lst->fa=&d[i];
d[i].c[0]=lst;
d[i].c[1]=null;
d[i].fa=&d[st[tp]];
d[st[tp]].c[1]=&d[i];
st[++tp]=i;
}
return &d[st[1]];
}
void init(Node *t){
if (t==null) return;
init(t->c[0]);
init(t->c[1]);
t->upd();
}
void change(Node *x,int _v){
x->v=_v;
while (x!=rt && x->v>x->fa->v)
x->rotate();
while (x->v<max(x->c[0]->v,x->c[1]->v)){
if (x->c[0]->v>x->c[1]->v)
x->c[0]->rotate();
else
x->c[1]->rotate();
}
for (;x!=null;x=x->fa)
x->upd();
}
int main(){
// freopen("in.txt","r",stdin);
// freopen("out.txt","w",stdout);
freopen("permutation.in","r",stdin);
freopen("permutation.out","w",stdout);
scanf("%d%d",&n,&Q);
for (int i=1;i<=n;++i)
scanf("%d",&p[i]);
rt=build();
init(rt);
printf("%d\n",rt->ans());
while (Q--){
int x;
scanf("%d",&x);
change(&d[x],p[x+1]);
change(&d[x+1],p[x]);
swap(p[x],p[x+1]);
// init(rt);
// for (int i=1;i<=n;++i)
// if (d[i].fa!=null)
// assert(d[i].fa->v>d[i].v);
printf("%d\n",rt->ans());
}
return 0;
}