题目传送-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
",L,R);
}
bool operator < (const SEG &a)const {
return L<a.L;
}
};
vector<SEG> seg[2];
inline bool Judge() {
//printf("Start
");
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
",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)