P11282 题解

· · 题解

一些题外话:赛时 20min 把这题秒了,加上 1A 共耗时 33min。坏消息是报名太晚,痛失首杀。最后 Div1 rk7,一分钱没拿到。

题目中把操作方式写得很抽象,实际上它等价于:每次选择排列中连续的三个数,将它们删除,并在原位放上它们的最小值或者最大值。

对排列 p 中每一个数单独处理。假设我们目前在处理位置 x 上的数,则令数列 {a_i} 为:

a_i=\begin{cases}0&p_i<p_x\\1&p_i=p_x\\2&p_i>p_x\end{cases}

那么显然原先的操作可以直接套用到现在的序列上,我们的目标是最后剩下的数是原序列中唯一的那个 1。此时,这个 1 只能和两个 0 或者两个 2 操作。

Part 1:x\equiv0\pmod 2

现在考虑一个只有 02 的长度为奇数的序列,对它操作可以得到什么结果。

注意到对三个 0 进行一次操作只能变成 0,对三个 2 进行一次操作只能变成 2,但是如果三个数中同时有 02,就可以变成任意一个。

这样我们发现,只要这个序列中有 0,就一定存在一种操作方法使得这个序列变成 02 同理。

如果 x\equiv 0\pmod 2,那么它两边的数列长度都为奇数。只要两边都能变成 0 或都能变成 2,那么就有解。

否则,只可能是一边全是 0,另一边全是 2。这样的话,出现对 0,1,2 进行操作就废了。然而,每次操作会减少 2 个数,而左边(不妨这样假设)0 的个数和右边 2 的个数都是奇数,因此无法在不进行 0,1,2 的操作且不把 1 搞没的前提下去掉所有的 0,因而无解。

维护前后缀最大最小值即可,时间复杂度 O(n)

Part 2:x\equiv 1\pmod 2

下证明:长度大于 2 且长度为偶数的由 02 组成的数列可以通过操作变成两个相等的数。

假设数列第一个数为 0,由之前对奇数长度数列性质的推导,只要后面有 0,就可以把去掉第一个数的部分变成 0。否则,后面全部都是 2,但数列长度大于 2,那么此时对去掉最后一个数的部分操作使其变成 2,就会剩下两个相同的数。

所以,如果 x\ne 3x\ne n-2,那么它两段的序列长度要么是 0,要么 >2,可以变成两个相同的数然后和中间的 1 操作把它留下来。

下只考虑 x=3 的情况。x=n-2 同理。

不妨假设 n\ge 7,此时 x 右边的序列长度 >2n=3n=5 的情况是类似的,最后会给出结论。

如果 (a_1,a_2,a_3)=(0,0,1)(2,2,1),那么把序列后半部分变成两个相同的数,显然有解。

否则,不妨设 (a_1,a_2,a_3)=(0,2,1)。显然我们不能对 a_1,a_2,a_3 进行操作,所以必定有一次操作是对 2,1,2 进行,此时 a_4=2。之后我们肯定还有一次对 0,1,0 进行的操作。

也就是说,我们必须从后面凑出一个 2 和一个 0。假设 p\le n+1 为最小的使得 \max_{i=4}^p{a_i}=2 的偶数(令 a_{n+1}=2),那么 a_4\sim a_p 的子序列长度为奇数,显然可以凑出一个 2。(这里 p=n+1 是个 corner case,不过不要紧,后面会把这种情况排除掉)

同时,由于 p 是最小的满足条件的偶数,所以把 a_4\sim a_{p-2} 合并凑不出 2,把 a_4\sim a_{p+2} 合并不优。现在,我们还需要在 a_{p+1}\sim a_n 中凑出一个 0。这个判断是简单的。

由此,我们解决了 n\ge 7 的情况。

n=3n=5x=3 时,有如下结论:

$n=5$ 时,有解当且仅当 $(a_1=a_2\wedge a_4=a_5)\vee (a_1=a_5\wedge a_2=a_4)$。 证明显然。 由此,我们在 $O(n)$ 的时间复杂度内解决了问题。 ```cpp #include<bits/stdc++.h> using namespace std; #define ll long long const ll N=1000007; ll T,n,p[N],premn[N],premx[N],sufmn[N],sufmx[N],ans[N]; int main(){ ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); cin>>T; while(T--){ cin>>n; for (int i=1;i<=n;++i){cin>>p[i];ans[i]=1;} premn[0]=sufmn[n+1]=1e9;premx[0]=sufmx[n+1]=0; for (int i=1;i<=n;++i){ premn[i]=min(premn[i-1],p[i]); premx[i]=max(premx[i-1],p[i]); } for (int i=n;i;--i){ sufmn[i]=min(sufmn[i+1],p[i]); sufmx[i]=max(sufmx[i+1],p[i]); } for (int i=2;i<=n;i+=2) if ((premx[i-1]<p[i]&&p[i]<sufmn[i+1])||(sufmx[i+1]<p[i]&&p[i]<premn[i-1])) ans[p[i]]=0; if (p[1]<p[3]&&p[2]>p[3]){ ans[p[3]]=0; ll pos=4; while(pos<n&&p[pos]<p[3]&&p[pos-1]<=p[3]) pos+=2; if (pos<n){ for (int i=pos+1;i<=n;++i) ans[p[3]]|=(p[i]<p[3]); } } if (p[1]>p[3]&&p[2]<p[3]){ ans[p[3]]=0; ll pos=4; while(pos<n&&p[pos]>p[3]&&p[pos-1]>=p[3]) pos+=2; if (pos<n){ for (int i=pos+1;i<=n;++i) ans[p[3]]|=(p[i]>p[3]); } } if (p[n]<p[n-2]&&p[n-1]>p[n-2]){ ans[p[n-2]]=0; ll pos=n-3; while(pos>0&&p[pos]<p[n-2]&&p[pos+1]<=p[n-2]) pos-=2; if (pos>0){ for (int i=1;i<pos;++i) ans[p[n-2]]|=(p[i]<p[n-2]); } } if (p[n]>p[n-2]&&p[n-1]<p[n-2]){ ans[p[n-2]]=0; ll pos=n-3; while(pos>0&&p[pos]>p[n-2]&&p[pos+1]>=p[n-2]) pos-=2; if (pos>0){ for (int i=1;i<pos;++i) ans[p[n-2]]|=(p[i]>p[n-2]); } } for (int i=1;i<=n;++i) cout<<ans[i];cout<<'\n'; } return 0; } ```