【AGC006E】 Rotate 3x3

本文深入探讨了一种针对特定网格变换的算法,通过列的黑白染色策略,实现了有效的状态转换和解决方案构造。文章详细介绍了变换操作,包括相邻列的翻转和交换,以及如何通过统计和冒泡排序类模拟计算翻转次数,最终判断解的存在性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Description

  
​   题目链接
  
  
  

Solution

  
​   显然每一列只能一起动,乱动则无解。
  
​   对原网格按列黑白染色,显然每一列数只能在相同颜色之间交换,乱动则无解。
  
​   之后考虑构造方案。
  
​   我们需要发(shou)现(wan)出一些好用的变换:
  
​   (1)使一种颜色的相邻两列同时上下翻转。
  
​   (2)使一种颜色的相邻两列交换,不翻转它们,而翻转另一个颜色中,不在这两列中间的,一个列。由于\(n \ge 5\),我们总能实现这个操作。
  
​   统计出黑色列总共需要使用(2)交换多少次达到目标、以及需要翻转多少次(由于使用(2)不影响上下状态,直接用初始状态作比较即可),记为\(flip_0\)\(inv_0\),同理对白色列计算\(flip_1\)\(inv_1\)
  
​   我们首先使用(2)逐一实现每一个\(flip_0\),每做一次\(flip_0\)的同时,\(inv_1\)也会减少一次。同理每做一次\(flip_1\)\(inv_0\)也会减少一次。
  
​   优先做完\(flip_0\)\(flip_1\),此时\(inv\)不论是正负都没关系(负也是有意义的),只要是偶数,就可以使用(1)逐个翻转回来。重要的是此时\(inv\)必须是偶数,这意味着没操作前,当且仅当\(flip_0\)\(inv_1\)的奇偶性相同,且\(flip_1\)\(inv_0\)的奇偶性相同时,才有解,否则无解。
  
​   对于\(flip\)的计算,使用冒泡排序类模拟的套路,通过统计原位置右边有多少已操作的数来计算出当前实际位置,从而确定每一次冒泡的步数。
  
​   总时间复杂度\(\mathcal O (n \log_2 n)\)
  
  
  

Code

    

#include <cstdio>
using namespace std;
const int N=100005;
int n;
int a[N][3],where[N];
int inv[2],flip[2];
inline int abs(int x){
    return x<0?-x:x;
}
void readData(){
    scanf("%d",&n);
    for(int j=0;j<3;j++)
        for(int i=1;i<=n;i++) scanf("%d",&a[i][j]);
}
bool preDraw(){
    for(int i=1;i<=n;i++){
        int id=(a[i][0]-1)/3+1;
        if((i-id)&1) return false;
        where[id]=i;
        int x=(id-1)*3+1,y=x+1,z=y+1;
        if(a[i][0]==x&&a[i][1]==y&&a[i][2]==z);
        else if(a[i][0]==z&&a[i][1]==y&&a[i][2]==x)
            inv[i&1]^=1;
        else return false;
    }
    return true;
}
namespace BIT{
    //suffix sum
    int a[N];
    inline void add(int u,int x){
        for(;u;u-=u&-u)
            a[u]+=x;
    }
    inline int que(int u){
        int res=0;
        for(;u&&u<=n;u+=u&-u)
            res+=a[u];
        return res;
    }
    inline void reset(){
        for(int i=1;i<=n;i++)
            a[i]=0;
    }
}
void calcFlip(){
    for(int i=1;i<=n;i+=2){
        int nowpos=where[i]+BIT::que(where[i])*2;
        flip[1]^=(abs(i-nowpos)/2)&1;
        BIT::add(where[i],1);
    }
    BIT::reset();
    for(int i=2;i<=n;i+=2){
        int nowpos=where[i]+BIT::que(where[i])*2;
        flip[0]^=(abs(i-nowpos)/2)&1;
        BIT::add(where[i],1);
    }
}
int main(){
    readData();
    if(!preDraw()){
        puts("No");
        return 0;
    }
    calcFlip();
    if(inv[0]!=flip[1]||inv[1]!=flip[0])
        puts("No");
    else 
        puts("Yes");
    return 0;
}

转载于:https://www.cnblogs.com/RogerDTZ/p/9487643.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值