zoukankan      html  css  js  c++  java
  • 【AT4363】[ARC102D] Revenge of BBuBBBlesort!(结论题)

    点此看题面

    • 给定一个长度为(n)的排列,一次操作可以选择一个(i)满足(p_{i-1}>p_i>p_{i+1}),然后交换(p_{i-1})(p_{i+1})
    • 要求判断是否能把这个排列转化为(1,2,...,n)
    • (nle3 imes10^5)

    重要结论

    我们只可能选择一开始就满足(p_i=i)的位置操作,证明就考虑反证。

    对于一个(p_i ot=i)的位置,如果我们操作了它,就会导致(p_{i-1}<p_i<p_{i+1})

    (p_i<i)的情况为例,我们想要把它向左移,就必须操作(i-2)使得(p_{i-1}>p_i),那么就必然导致(p_{i-2}<p_{i-1}),因此不可能再把(i)往左移了,必然无解。

    对于一个若干次操作后(p_i=i)的位置,则这个位置必然是跟(p_{i-2})(p_{i+2})交换来的。

    若跟(p_{i-2})交换来则(p_i>p_{i-1}),若跟(p_{i+2})交换来则(p_i<p_{i+1}),不可能满足(p_{i-1}<p_i<p_{i+1}),无法操作。

    区间划分+推结论

    根据先前的结论,如果有三个连续位置都满足(p_i ot=i),那么中间的位置始终无法被交换到,肯定无解。

    否则,我们可以把连续一段(01010...010)取出来,当做一个独立的区间处理。

    考虑判断一个区间([l,r])是否有解,首先一个必要条件就是所有数都必须在([l,r])范围内。

    然后我们还需要一个条件,就是仅保留(p_i ot=i)的位置时最长下降子序列长度不能超过(2)

    首先这是必要的,因为存在一个长度为(3)的下降子序列时,肯定无法让它们全部到自己位置上。

    其实这是充分的,因为一次交换让相邻两个元素变得有序,不可能使得最长下降子序列变大,那么当它长度变为(1)时就达成了条件。

    代码:(O(n))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 300000
    using namespace std;
    int n,p[N+5];
    namespace FastIO
    {
    	#define FS 100000
    	#define tc() (FA==FB&&(FB=(FA=FI)+fread(FI,1,FS,stdin),FA==FB)?EOF:*FA++)
    	char oc,FI[FS],*FA=FI,*FB=FI;
    	Tp I void read(Ty& x) {x=0;W(!isdigit(oc=tc()));W(x=(x<<3)+(x<<1)+(oc&15),isdigit(oc=tc()));}
    	Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    }using namespace FastIO;
    int f[2];I void Solve(CI l,CI r)
    {
    	#define NA() (puts("No"),exit(0),0)//无解
    	if(l>r) return;RI i,Mn=n,Mx=1;for(i=l;i<=r;i+=2) Mn=min(Mn,p[i]),Mx=max(Mx,p[i]);(Mn^l||Mx^r)&&NA();//如果值不全在[l,r]范围内无解
    	for(f[0]=f[1]=0,i=l;i<=r;i+=2) p[i]>f[0]?f[0]=p[i]:(p[i]>f[1]?f[1]=p[i]:NA());//维护最长下降子序列判断长度是否会超过2
    }
    int main()
    {
    	RI i,j;for(read(n),i=1;i<=n;++i) read(p[i]);
    	for(i=1;i+2<=n;++i) if(p[i]^i&&p[i+1]^(i+1)&&p[i+2]^(i+2)) return puts("No"),0;//连续三个p[i]≠i无解
    	for(i=1;i<n;++i) if(p[i]^i&&p[i+1]==i+1) {for(j=i;i+2<=n&&p[i+1]==i+1&&p[i+2]^(i+2);i+=2);Solve(j,i);}//抠出每段区间处理
    	return puts("Yes"),0;
    }
    
    败得义无反顾,弱得一无是处
  • 相关阅读:
    物理机装kali复盘
    20179214《网络攻防实践》课下练习
    20179214《网络攻防实践》第三次学习总结
    20179214 2017-2018-2 《密码与安全新技术》第一周作业
    《网络攻防实践》小工具学习
    2017-2018 2 20179214《网络实践攻防》第三周作业(二)
    2017-2018 2 20179214《网络实践攻防》第三周作业(一)
    20179214《网络攻防实践》第二周学习总结
    第一章
    kali-linux简单学习(二)
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/AT4363.html
Copyright © 2011-2022 走看看