[agc006E]Rotate 3x3

Description

给你一个3*N的网格,位置为(i,j)的网格上的数为i+3(j-1)。每次选一个3*3的网格旋转180度,问最后能否使得网格(i,j)的值为ai,j。(5N105

如图:

 

Solution

依图可看出,所谓的旋转就是将选择的3*3网格左右列交换,并且3列都进行翻转。

设正列(如1,2,3)为小写字母,反列(如3,2,1)为大写字母。

假如有相邻5列:

a b c d e

C B A d e

C B E D a

e b c D a

e b A d C

a B E d C

a B c D e

a d C b e

c D A b e

c B a d e

A b C d e

我们可以在有5列可供操纵的情况下将任意相隔1列的两列翻转而不影响其他。

在最终答案中设下标为奇的反列个数为x,下表为偶的个数为y。

先不考虑翻转问题,将奇列和偶列分开考虑(因为在处理奇列的时候只会翻转却不会影响偶列的具体数值)。由于如果初始矩阵操作后变为矩阵a,则矩阵a一定能变为初始矩阵,我们按照列的权值从小到大将矩阵a恢复为初始矩阵。处理奇列时,每翻转一次,就会翻转一个偶列,偶列也是同样道理。我们计算出奇列、偶列被翻转次数的奇偶性,与x、y比较即可。

Code

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
int n,a[3][100010];
int need_swap[2],real_swap[2];
int num[100010],id[100010];
int tree[100010];
void add(int _id,int x){for(;_id<=n;_id+=_id&-_id) tree[_id]+=x;}
int query(int _id){int re=0;for(;_id;_id-=_id&-_id) re+=tree[_id];return re;}
int main()
{
    scanf("%d",&n);
    for (int j=0;j<3;j++)
    for (int i=1;i<=n;i++) 
        scanf("%d",&a[j][i]);
    for (int i=1;i<=n;i++)
    for (int j=0;j<3;j++)
    {
        if ((a[0][i]^i)&1) { printf("No");return 0;}
        if (a[0][i]==a[1][i]+1&&a[1][i]==a[2][i]+1&&a[2][i]%3!=0) 
        {
            need_swap[i&1]++;num[i]=a[2][i]/3+1;id[num[i]]=i;continue;
        }
        if (a[0][i]==a[1][i]-1&&a[1][i]==a[2][i]-1&&a[0][i]%3!=0) 
        {
            num[i]=a[0][i]/3+1;id[num[i]]=i;continue;
        }
        printf("No");return 0;
    }
    int cnt;
    for (int i=1;i<=n;i+=2)
    {
        cnt=id[i]+2*(i/2-query(id[i]));
        real_swap[0]+=abs(i-cnt)/2;
        add(id[i],1);
    }
    memset(tree,0,sizeof(tree));
    for (int i=2;i<=n;i+=2)
    {
        cnt=id[i]+2*(i/2-1-query(id[i]));
        real_swap[1]+=abs(i-cnt)/2;
        add(id[i],1);
    }
    real_swap[0]%=2;real_swap[1]%=2;need_swap[0]%=2;need_swap[1]%=2;
    if (real_swap[0]!=need_swap[0]||real_swap[1]!=need_swap[1]) printf("No");
    else printf("Yes");
}

 

posted @ 2018-08-16 16:06  _雨后阳光  阅读(314)  评论(0编辑  收藏  举报