[ZJOI 2005] 梦幻折纸

题目传送-Luogu

题意:

给一个 \(n*m\) 的网格图,每个网格上有 \([1,n*m]\) 的数字,且每个都出现且恰好出现一次.
显然进行若干次折叠直到剩下一个 \(1*1\) 的小网格时,它在纵向上有 \(n*m\) 层.
那么能否安排一种折叠方案,使得这 \(n*m\) 层从上往下的标号恰好为 \(1\)\(n*m\).

题解:

显然每个纵向和横向的格线都会被折到.我们观察剩下的的那个小方格,可以发现每层都有向左或右,上或下的连接,而且上下的连接和左右的连接是无关的.
然而还是没有什么卵用...
这时我们考虑什么情况下会不存在合法的方案,先举个例子:1 3 4 2.
我们想象面前有个 \(1*4\) 的纸条,先把 3 折到 1 的下面,再把 4 折到 3 的下面,这时我们发现 2 居然折不进去了.
这是因为 \(4,2\)\(1,3\) 的连接是同向的,那么这样就不能交叉进去了.
那么我们发现折痕的方向是相邻的格线两两相反的(折一折就知道了)
根据上面的结论,相同的方向的折痕不能存在相交.这样我们就会只有一条的情况了.

对于一般情况,我们发现当一条格线被决策后,它所在的所有格子都会被强制决策.
由于行和列是互不影响的,那么这个就很好扩展了.

过程:

想了超级久的..

代码:

#define GG {puts("Cheat"); continue;}
const int N=110;
int n,m;
int G[N][N];
struct SEG {
  int L,R;
  inline void Config() {
    if(L>R) swap(L,R);
  }
  inline void Print() {
    printf("%d %d\n",L,R);
  }
  bool operator < (const SEG &a)const {
    return L<a.L;
  }
};
vector<SEG> seg[2];
inline bool Judge() {
  //printf("Start\n");
  for(int t=0;t<2;t++) {
    sort(seg[t].begin(),seg[t].end());
    //for(int i=0;i<(int)seg[t].size();i++) seg[t][i].Print();
    vector<pii> ref;
    for(int i=0;i<(int)seg[t].size();i++) {
      ref.pb(mp(seg[t][i].L,i+1));
      ref.pb(mp(seg[t][i].R,-i-1));
    }
    sort(ref.begin(),ref.end());
    assert(ref.size()==seg[t].size()*2);
    stack<int> stk;
    for(int i=0;i<(int)ref.size();i++) {
      //printf("Ask::%d %d\n",ref[i].F,ref[i].S);
      if(ref[i].S>0) stk.push(ref[i].S);
      else {
	if(stk.top()!=-ref[i].S) return false;
	stk.pop();
      }
    }
  }
  return true;
}
signed main() {
  int T; read(T);
  for(int cas=1;cas<=T;cas++) {
    read(n); read(m);
    for(int i=1;i<=n;i++)
      for(int j=1;j<=m;j++)
	read(G[i][j]);
    seg[0].clear(); seg[1].clear();
    for(int i=1;i<n;i++)
      for(int j=1;j<=m;j++)
	seg[i&1].pb((SEG){G[i][j],G[i+1][j]});
    for(int i=0;i<2;i++)
      for(int j=0;j<(int)seg[i].size();j++)
	seg[i][j].Config();
    if(!Judge()) GG;

    seg[0].clear(); seg[1].clear();
    for(int j=1;j<m;j++)
      for(int i=1;i<=n;i++)
	seg[j&1].pb((SEG){G[i][j],G[i][j+1]});
    for(int i=0;i<2;i++)
      for(int j=0;j<(int)seg[i].size();j++)
	seg[i][j].Config();
    if(!Judge()) GG;
    puts("AllRight");
  }
  return 0;
}

用时: \(\infty\)

posted @ 2019-03-29 17:36  functionendless  阅读(356)  评论(0编辑  收藏  举报