【做题】agc006e - Rotate 3x3——分析&思维

原文链接 https://www.cnblogs.com/cly-none/p/9800105.html

题意:给出一个三行\(n\)列的矩阵。问它能否由满足\(a_{ij} = 3(j-1) + i\)的初始矩阵通过有限次中心对称其中的一个\(3 \times 3\)子矩阵的操作得到。

\(5 \leq n \leq 10^5\)

首先,我们容易发现,无论如何操作,每一列都是形如\(3k+1, \, 3k+2, \, 3k+3\)的3个数,并且要么是顺序要么是倒序。我们记恰是原来第\(i\)列的一列的权值为\(i\),恰是原来第\(i\)列上下翻转为\(-i\)。那么,我们每一次操作,就变成了选取相邻的3列,交换左边和右边两列的权值并把这3个权值取负。

然后,我们还能得到偶数权值只可能存在于偶数列中,奇数权值只存在于奇数列中。

然而,仅此还不足以成为充分条件,我们需要进一步分析。

一般而言,这类问题的关键在于发现“不变量”,即一次操作前后都不会发生变化的量。

于是考虑一次操作。它由两个部分组成:把所有列按奇偶性分为两组后,交换并取反一组中相邻的两列;并取反另一组中对应位置的一列。让我们先勉为其难地放弃充分性,分开考虑这两部分而忽略一部分联系:

  • 操作1:交换两个相邻元素。
  • 操作2:取反一个元素。

这两个操作都很简洁,容易分析。

对于操作1,通过逆序对分析我们能得到,如果初始状态和结束状态已知,那么它操作总数的奇偶性是固定的。同样,操作2也有这个性质。

同时,我们注意到,奇数列中操作1的个数等于偶数列中操作2的个数。这意味着,奇数列的逆序对数的奇偶性与偶数列中负数个数的奇偶性相同。反之亦然。

又是一个充分条件,但已经挺复杂了。利用搜索程序验证,在\(n\)很小时,这个条件是必要的。

因此我们尝试证明这个条件是必要的。事实上,既然都写出搜索,我们就能发现在\(n=5\)时,这两种情况是可能的:
\[ \begin{matrix} -1 & 2 & -3 & 4 & 5 \\ 1 & -2 & 3 & -4 & 5 \end{matrix} \]
这意味着,只要先把所有元素移动到对应位置,就可以不断任取同奇偶的两列调整正负。因为奇偶性是保证的,所以一定能得到最终状态。所以本题标算搜索

时间复杂度\(O(n \log n)\)

#include <bits/stdc++.h>
using namespace std;
const int N = 100010;
int mat[4][N],val[N],n,num[2],v[N];
#define lowbit(x) ((x) & (-(x)))
void add(int p,int val) {
  for ( ; p <= n ; p += lowbit(p))
    v[p] += val;
}
int query(int p) {
  int ret = 0;
  for ( ; p ; p -= lowbit(p))
    ret += v[p];
  return ret;
}
void fail() {
  puts("No");
  exit(0);
}
int main() {
  scanf("%d",&n);
  for (int i = 1 ; i <= 3 ; ++ i)
    for (int j = 1 ; j <= n ; ++ j)
      scanf("%d",&mat[i][j]);
  for (int i = 1 ; i <= n ; ++ i) {
    if (mat[1][i] % 3 == 0) {
      if (mat[2][i] == mat[1][i] - 1 && mat[3][i] == mat[1][i] - 2)
    val[i] = - mat[1][i] / 3;
      else fail();
    } else if (mat[3][i] % 3 == 0) {
      if (mat[2][i] == mat[3][i] - 1 && mat[1][i] == mat[3][i] - 2)
    val[i] = mat[3][i] / 3;
      else fail();
    } else fail();
  }
  for (int i = 1 ; i <= n ; i += 2) {
    if (val[i] < 0) num[1] ^= 1, val[i] = -val[i];
    if (val[i] % 2 == 0) fail();
  }
  for (int i = 2 ; i <= n ; i += 2) {
    if (val[i] < 0) num[0] ^= 1, val[i] = -val[i];
    if (val[i]&1) fail();
  }
  for (int i = 1 ; i <= n ; i += 2) {
    if ((query(n) - query(val[i]))&1) num[0] ^= 1;
    add(val[i],1);
  }
  memset(v,0,sizeof v);
  for (int i = 2 ; i <= n ; i += 2) {
    if ((query(n) - query(val[i]))&1) num[1] ^= 1;
    add(val[i],1);
  }
  if ((!num[0]) && (!num[1]))
    puts("Yes");
  else puts("No");
  return 0;
}


小结:本题的解法主要有两点存在启发性:一是推导充分条件时,可以暂时放弃充分性来得到新的推论(这和求和时引入新的\(\sum\)有异曲同工之妙);二是一个搜索程序无伤大雅,除找规律乱搞外,还能成为解题的助力。

posted @ 2018-10-16 19:31 莫名其妙的aaa 阅读(...) 评论(...) 编辑 收藏