【性质发掘+树状数组】AGC006E

好烦啊\(O(n^2)\)跑得比\(O(nlogn)\)快【】
先把一些SB情况判掉

首先可以求出这个矩阵中每一列将被放到哪一列上了

于是先把列号变成目标状态趴OwO

然后你看这个\(3\times 3\),每次修改只会交换奇偶性相同两列的位置,可以尝试一下分奇偶讨论,分别求出把奇偶列变成目标状态的次数,树状数组逆序对

于是设\(cnt[0/1]\)表示列号为奇数/偶数的逆序列个数的奇偶性

注意到每次翻转会改变\(cnt[中间列列号%2]\),就修改一下

把列全部复位后,可以把连续五列中,在不改变其他列的情况下,把任意相隔一列的两列数同时变成逆序

具体过程见下,设列\(A,B,C,D,E\)分别为列\(a,b,c,d,e\)的逆序列:

\(\texttt{a b c d e}\)

\(\texttt{C B A d e}\)

\(\texttt{C B E D a}\)

\(\texttt{e b c D a}\)

\(\texttt{e b A d C}\)

\(\texttt{a B E d C}\)

\(\color{red}{\texttt{a B c D e}}\)

\(\texttt{a d C b e}\)

\(\texttt{c D A b e}\)

\(\texttt{c B a d e}\)

\(\color{red}{\texttt{A b C d e}}\)

最后就判断一下\(cnt[0],cnt[1]\)是不是0就行了

代码:

#include <bits/stdc++.h>
#define N 100005
#define For(i,x,y) for(int i=(x);i<=(y);++i)
#define Rof(i,x,y) for(int i=(x);i>=(y);--i)
#define NosoL return puts("No"),0
#define SoL return puts("Yes"),0
using namespace std;
int a[4][N],cnt[2],to[N],n,c[N];
void ins(int x){ for(int i=x;i<=n;i+=(i&(-i))) c[i]++; }
int qry(int x){ int res=0;for(int i=x-1;i;i-=(i&(-i)))res+=c[i];return res; }
int main(){
    scanf("%d",&n);
    For(i,1,3) For(j,1,n) scanf("%d",&a[i][j]);
    For(i,1,n){
        if(abs(a[1][i]-a[2][i])!=1 || abs(a[2][i]-a[3][i])!=1) NosoL;
        For(j,1,3) if(a[j][i]%2!=(i+3*j-3)%2) NosoL;
        if(a[1][i]%3==1 && a[1][i]>a[2][i]) NosoL;
        if(a[1][i]%3==0 && a[1][i]<a[2][i]) NosoL;
        to[i]=(a[2][i]+1)/3;
        cnt[i&1]^=(a[1][i]>a[3][i]);    
    }
    For(i,1,n) if(to[i]%2!=i%2) NosoL;
    for(int i=n-(n%2==0);i>=1;i-=2){
        int x=qry(to[i]);
        cnt[0]^=(x&1);
        ins(to[i]);
    }
    For(i,1,n) c[i]=0;
    for(int i=n-(n&1);i>=1;i-=2){
        int x=qry(to[i]);
        cnt[1]^=(x&1);
        ins(to[i]);
    }
    if(!cnt[0] && !cnt[1]) SoL;
    else NosoL;
}

猜你喜欢

转载自www.cnblogs.com/PsychicBoom/p/12037511.html
今日推荐