fsy's blog

燃峥嵘岁月,烈日终破云。

CSP-S 2020, NOIP 2020 题解

CSP-S 2019,T1:使用 unsigned long long

CSP-S 2020,T2:需特判 $2^{64}$

NOIP 2020,T1:需要使用简单高精度

NOIP 2021:????

CSP-S 2020

T4 暂缺。

【T1】 儒略历

模拟,注意细节即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
#include <bits/stdc++.h>
#define LL long long
#define ULL unsigned long long
#define LD long double
#define reg register
#define _rep(i, x, y) for(int i = x; i <= y; i++)
#define _per(i, x, y) for(int i = x; i >= y; i--)
template <typename T>
inline void read(T &x) {
x = 0; T f = (T) 1;
char c = getchar();
for(; !isdigit(c); c = getchar()) {if(c == '-') f = -f;}
for(; isdigit(c); c = getchar()) x = x * 10 + c - 48;
x *= f;
}
template <typename T, typename... Args>
inline void read(T& x, Args&... args) {
read(x); read(args...);
}
template <typename T>
inline void write(T x) {
if(x < 0) {x = -x; putchar('-');}
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T>
inline void writesp(T x, char sp = ' ') {write(x); putchar(sp);}

const int days[13] = {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}, Limit = 2299161;
int T, y = -4713, m = 1, d = 1;
LL N;

bool is_leap(int y, int k) {
if(y < 0) ++y;
if(k == 1) return (y % 4 == 0);
return (y % 400 == 0 || (y % 4 == 0 && y % 100));
}
void Julian(LL D) {
y += (D / 1461) * 4; D %= 1461;
if(y >= 0) ++y;
while(D >= 365 + is_leap(y, 1)) D -= (365 + is_leap(y, 1)), y += (y == -1 ? 2 : 1);
while(D >= days[m] + (is_leap(y, 1) && m == 2)) D -= (days[m] + (is_leap(y, 1) && m == 2)), m = m % 12 + 1;
d += D;
}
void Gregorian(LL D) {
while(D && y < 1583) {
++d; --D;
if(d > days[m]) ++m, d = 1;
if(m > 12) ++y, m = 1;
}
y += (D / 146097ll) * 400, D %= 146097ll;
while(D >= 365 + is_leap(y, 2)) D -= (365 + is_leap(y, 2)), ++y;
while(D >= days[m] + (is_leap(y, 2) && m == 2)) D -= days[m] + (is_leap(y, 2) && m == 2), m = m % 12 + 1, y += (m == 1);
d += D;
}
void print_time(int y, int m, int d) {
printf("%d %d %d", d, m, abs(y)); puts(y < 0 ? " BC" : "");
}

int main() {
freopen("julian.in", "r", stdin);
freopen("julian.out", "w", stdout);
read(T);
while(T--) {
read(N);
if(N < Limit) {
y = -4713, m = 1, d = 1;
Julian(N);
} else {
y = 1582, m = 10, d = 15;
Gregorian(N - Limit);
}
print_time(y, m, d);
}
return 0;
}

【T2】 动物园

对每一个二进制位分开考虑;明显,如果不存在一个动物编号 $a_i$ 使得此二进制位的值为 $1$,并且《饲养指南》里存在对这个二进制位的要求,那么不能饲养编号含这个二进制位的动物,反之可以。

记合法二进制位数为 $cnt$,则答案为 $2^{cnt}$。

将每个 $a_i$ 二进制分拆即可。时间复杂度 $O(n \log \max(a_i))$。

注意特判答案为 $2^{64}$ 的情况。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <bits/stdc++.h>
#define LL long long
#define ULL unsigned long long
#define LD long double
#define reg register
#define _rep(i, x, y) for(int i = x; i <= y; i++)
#define _per(i, x, y) for(int i = x; i >= y; i--)
template <typename T>
inline void read(T &x) {
x = 0; T f = (T) 1;
char c = getchar();
for(; !isdigit(c); c = getchar()) {if(c == '-') f = -f;}
for(; isdigit(c); c = getchar()) x = x * 10 + c - 48;
x *= f;
}
template <typename T, typename... Args>
inline void read(T& x, Args&... args) {
read(x); read(args...);
}
template <typename T>
inline void write(T x) {
if(x < 0) {x = -x; putchar('-');}
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T>
inline void writesp(T x, char sp = ' ') {write(x); putchar(sp);}

const int maxN = 1e6 + 10;
int n, m, c, k, bits[65], b[65];
ULL a[maxN], q[maxN];
void Ins(ULL x) {
int cnt = 0;
while(x) {
if(x & 1) bits[cnt] = 1;
++cnt; x >>= 1;
}
}

int main() {
freopen("zoo.in", "r", stdin);
freopen("zoo.out", "w", stdout);
read(n, m, c, k);
_rep(i, 1, n) read(a[i]), Ins(a[i]);
_rep(i, 1, m) {int p, q; read(p); read(q); b[p] = true;}
int B = 0;
ULL ans = 1;
_rep(i, 0, k - 1) {
if(bits[i] == 0 && b[i] == 1) continue;
ans = ans * 2ull;
}
if(n == 0 && k == 64 && m == 0) puts("18446744073709551616");
else {
ans -= n;
writesp(ans, '\n');
}
return 0;
}

【T3】 函数调用

一道好题。

题目中提示不会出现递归,故所有函数的调用关系形成一个 DAG。

如果只有 2、3 两类函数,那么我们可以用拓扑排序或者 dfs 直接算出某个函数对答案的影响。

如果只有 1、3 两类函数,那么我们可以用拓扑排序求出每个第 1 类函数的执行次数。

接下来的讲解先鸽着,有时间来填。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#include <bits/stdc++.h>
#define LL long long
#define ULL unsigned long long
#define LD long double
#define reg register
#define _rep(i, x, y) for(int i = x; i <= y; i++)
#define _per(i, x, y) for(int i = x; i >= y; i--)
template <typename T>
inline void read(T &x) {
x = 0; T f = (T) 1;
char c = getchar();
for(; !isdigit(c); c = getchar()) {if(c == '-') f = -f;}
for(; isdigit(c); c = getchar()) x = x * 10 + c - 48;
x *= f;
}
template <typename T, typename... Args>
inline void read(T& x, Args&... args) {
read(x); read(args...);
}
template <typename T>
inline void write(T x) {
if(x < 0) {x = -x; putchar('-');}
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T>
inline void writesp(T x, char sp = ' ') {write(x); putchar(sp);}

const int maxN = 1e5 + 10, maxM = 1e5 + 10, maxQ = 1e5 + 10, maxC = 1e6 + 10, P = 998244353;
int n, m, Q, f[maxQ], a[maxN], opt[maxM], p[maxM], v[maxM], dfn[maxM], mult[maxM], cnt[maxM], called[maxM];
std::vector<int> relations[maxN];

int sz = 1, head[maxM], deg[maxM];
struct Edge {
int from, to, nxt;
Edge() {}
Edge(int _f, int _t, int _n) : from(_f), to(_t), nxt(_n) {}
} edge[maxC];
void add_edge(int u, int v) {
edge[++sz] = Edge(u, v, head[u]);
head[u] = sz; ++deg[v];
}

void topo() {
std::queue<int> q;
_rep(i, 1, Q) called[f[i]] = 1;
_rep(i, 1, m) if(!deg[i]) q.push(i);
while(!q.empty()) {
int u = q.front(); q.pop();
for(int i = head[u]; i; i = edge[i].nxt) {
int v = edge[i].to; --deg[v]; called[v] = called[v] | called[u];
if(!deg[v]) q.push(v);
}
}
memset(deg, 0, sizeof(deg));
_rep(i, 1, m) {
if(called[i]) {
int len = relations[i].size();
_rep(j, 0, len - 1) ++deg[relations[i][j]];
}
}
while(!q.empty()) q.pop();
_rep(i, 1, m) if(called[i] && !deg[i]) q.push(i);
while(!q.empty()) {
int u = q.front(); q.pop(); dfn[++dfn[0]] = u;
for(int i = head[u]; i; i = edge[i].nxt) {
int v = edge[i].to; --deg[v];
if(!deg[v]) q.push(v);
}
}
}
int main() {
freopen("call.in", "r", stdin);
freopen("call.out", "w", stdout);
read(n); _rep(i, 1, n) read(a[i]);
read(m);
_rep(i, 1, m) {
read(opt[i]);
if(opt[i] == 1) read(p[i], v[i]);
else if(opt[i] == 2) read(v[i]);
else if(opt[i] == 3) {
int c, x; read(c);
_rep(j, 1, c) read(x), relations[i].push_back(x), add_edge(i, x);
}
}
read(Q);
_rep(i, 1, Q) read(f[i]);

topo();
int tot_mul = 1;
_rep(i, 1, m) mult[i] = (opt[i] == 2 ? v[i] : 1);
_per(i, dfn[0], 1) {
int x = dfn[i], len = relations[x].size();
_rep(j, 0, len - 1) mult[x] = 1ll * mult[x] * mult[relations[x][j]] % P;
}
_per(i, Q, 1) {
int x = f[i]; cnt[x] = (cnt[x] + tot_mul % P) % P, tot_mul = 1ll * tot_mul * mult[x] % P;
}
int now_mul = 1;
_rep(i, 1, dfn[0]) {
int x = dfn[i], len = relations[x].size(); now_mul = 1;
_per(j, len - 1, 0) {
int v = relations[x][j];
cnt[v] = (cnt[v] + 1ll * now_mul * cnt[x] % P) % P;
now_mul = 1ll * now_mul * mult[v] % P;
}
}
_rep(i, 1, n) a[i] = 1ll * a[i] * tot_mul % P;
_rep(i, 1, m) {
if(opt[i] == 1) a[p[i]] = (a[p[i]] + 1ll * v[i] * cnt[i] % P) % P;
}
_rep(i, 1, n) writesp(a[i], ' ');
puts("");
return 0;
}

【T4】 贪吃蛇

NOIP 2020

【T1】 排水系统

拓扑排序后,依次处理即可。

注意答案分母会达到 $60^{11}$,需要使用简单高精度。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
#include <bits/stdc++.h>
#define LL long long
#define ULL unsigned long long
#define LD long double
#define reg register
#define _rep(i, x, y) for(int i = x; i <= y; i++)
#define _per(i, x, y) for(int i = x; i >= y; i--)
template <typename T>
inline void read(T &x) {
x = 0; T f = (T) 1;
char c = getchar();
for(; !isdigit(c); c = getchar()) {if(c == '-') f = -f;}
for(; isdigit(c); c = getchar()) x = x * 10 + c - 48;
x *= f;
}
template <typename T, typename... Args>
inline void read(T& x, Args&... args) {
read(x); read(args...);
}
template <typename T>
inline void write(T x) {
if(x < 0) {x = -x; putchar('-');}
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T>
inline void writesp(T x, char sp = ' ') {write(x); putchar(sp);}

const LL BASE = (1ll << 53) - 1;
struct BigNum {
LL hi, lo;
void initialize(LL a = 0, LL b = 1) {hi = a; lo = b;}
void operator += (BigNum B) {
lo += B.lo; hi = hi + B.hi + (lo >> 53); lo &= BASE;
}
friend int operator % (BigNum A, int mod) {
return (((A.hi % mod) << 53) + A.lo % mod) % mod;
}
friend BigNum operator / (BigNum A, int div) {
A.lo = ((A.hi % div) << 53) + A.lo;
A.lo /= div; A.hi /= div; return A;
}
void print() {
LL l = lo, h = hi;
LL x = l - h * 992800745259008ll;
while(x < 0) --h, x += 1e16;
if(h) printf("%lld%016lld", h, x);
else printf("%lld", x);
}
};

const int maxN = 1e5 + 10;
int n, m, deg[maxN], topo[maxN], vis[maxN], t = 0;
BigNum frac[maxN];
std::vector<int> G[maxN];

int main() {
freopen("water.in", "r", stdin);
freopen("water.out", "w", stdout);
read(n, m);
_rep(i, 1, n) {
int w; read(w);
_rep(j, 1, w) {
int x; read(x);
++deg[x]; G[i].push_back(x);
}
}

_rep(i, 1, m) topo[++t] = i;
int ptr = 1;
while(ptr <= t) {
int l = G[topo[ptr]].size();
_rep(i, 0, l - 1) {
int tar = G[topo[ptr]][i];
--deg[tar];
if(!deg[tar]) topo[++t] = tar;
}
++ptr;
}

_rep(i, 1, m) frac[i].initialize(4027, 7714201158025216ll);
_rep(i, m + 1, n) frac[i].initialize(0, 0);
_rep(i, 1, n) {
int t = topo[i];
int l = G[t].size();
if(!l) continue;
BigNum prob = frac[t] / l;
_rep(j, 0, l - 1) {
frac[G[t][j]] += prob;
}
}
_rep(i, 1, n) {
if(G[i].size() == 0) {
BigNum B; B.initialize(4027, 7714201158025216ll);
while(B % 2 == 0 && frac[i] % 2 == 0) frac[i] = frac[i] / 2, B = B / 2;
while(B % 3 == 0 && frac[i] % 3 == 0) frac[i] = frac[i] / 3, B = B / 3;
while(B % 5 == 0 && frac[i] % 5 == 0) frac[i] = frac[i] / 5, B = B / 5;
frac[i].print(); putchar(' '); B.print(); putchar('\n');
}
}
return 0;
}

【T2】 字符串匹配

一种做法是扩展 KMP,此做法时间复杂度为 $O(n)$,此处略去不讲。

枚举 $A + B$ 长度,并且每次二分其 $i$ 的最大值。这里是 $O(\sum_{i=1}^{n} \log(\frac{n}{i}))$ 的,简单分析可知这个是 $O(n)$ 的。

同时可知,在 $i$ 同为奇数时,拆分 $A$、$B$ 的方案数是一样的,偶数同理。故可以 $O(\log 26)$ 统计贡献。

故本题解决。时间复杂度 $O(n \log 26)$。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
#include <bits/stdc++.h>
#define LL long long
#define ULL unsigned long long
#define LD long double
#define reg register
#define _rep(i, x, y) for(int i = x; i <= y; i++)
#define _per(i, x, y) for(int i = x; i >= y; i--)
template <typename T>
inline void read(T &x) {
x = 0; T f = (T) 1;
char c = getchar();
for(; !isdigit(c); c = getchar()) {if(c == '-') f = -f;}
for(; isdigit(c); c = getchar()) x = x * 10 + c - 48;
x *= f;
}
template <typename T, typename... Args>
inline void read(T& x, Args&... args) {
read(x); read(args...);
}
template <typename T>
inline void write(T x) {
if(x < 0) {x = -x; putchar('-');}
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T>
inline void writesp(T x, char sp = ' ') {write(x); putchar(sp);}

const int maxN = 1048586, Base[2] = {137, 33827};
int t, n, F[maxN], Fsuf[maxN], buk[26];
char str[maxN];

struct Hash {
unsigned int bas[maxN], has[maxN];
void init(int opt) {
bas[0] = 1; has[0] = 0;
_rep(i, 1, n) bas[i] = bas[i - 1] * Base[opt], has[i] = has[i - 1] * Base[opt] + str[i];
}
int HashVal(int l, int r) {return has[r] - has[l - 1] * bas[r - l + 1];}
} h1, h2;

// fenwick tree
int Sigma = 27;
struct BIT {
int tree[34];
void init() {memset(tree, 0, sizeof(tree));}
void modify(int pos, int val) {
for(; pos <= Sigma; pos += (pos & -pos)) tree[pos] += val;
}
int query(int pos) {
int ans = 0;
for(; pos; pos -= (pos & -pos)) ans += tree[pos];
return ans;
}
} fenwick;
int main() {
read(t);
while(t--) {
scanf("%s", str + 1); n = strlen(str + 1);
h1.init(0);
h2.init(1);

memset(buk, 0, sizeof(buk)); F[0] = 0;
_rep(i, 1, n) buk[str[i] - 'a']++, F[i] = F[i - 1] + (buk[str[i] - 'a'] & 1 ? 1 : -1);
memset(buk, 0, sizeof(buk)); Fsuf[n + 1] = 0;
_per(i, n, 1) buk[str[i] - 'a']++, Fsuf[i] = Fsuf[i + 1] + (buk[str[i] - 'a'] & 1 ? 1 : -1);

fenwick.init(); fenwick.modify(2, 1);
LL ans = 0;
_rep(i, 2, n - 1) {
int L = 1, R = n / i;
while(L < R) {
int mid = (L + R + 1) >> 1;
if(h1.HashVal(1, i * (mid - 1)) == h1.HashVal(i + 1, i * mid) && h2.HashVal(1, i * (mid - 1)) == h2.HashVal(i + 1, i * mid)) {
L = mid;
} else {
R = mid - 1;
}
}
int C = Fsuf[i + 1], qwq = fenwick.query(C + 1);
ans += 1ll * qwq * ((L + 1) >> 1);
if(i * L == n && (L & 1)) ans -= qwq;
if(L >= 2) {
int D = Fsuf[2 * i + 1]; qwq = fenwick.query(D + 1);
ans += 1ll * qwq * (L >> 1);
if((i * L == n) && !(L & 1)) ans -= qwq;
}
fenwick.modify(F[i] + 1, 1);
}
writesp(ans, '\n');
}
return 0;
}

【T3】 移球游戏

Dzhao 的做法。

考虑只有两种球的情况,不难构造出一种 $5m - t_1$ 步的做法,其中 $t_1$ 为 1 号柱子中的 1 号球的个数:

  • 第一步:将 2 号柱子中顶部 $t_1$ 个球移至 3 号柱子,耗费 $t_1$ 步。
  • 第二步:将 1 号柱子里所有球按如下规则移至 2、3 号柱子,耗费 $m$ 步:若柱子顶部为 1 号球,则将该球移至 2 号柱子,反之移至 3 号柱子。
  • 第三步:将 2、3 号柱子中原来属于 1 号柱子里的球全部移回 1 号柱子,耗费 $m$ 步。注意:这一步需要让 1 号球与 2 号球分离。
  • 第四步:将 2 号柱子里的所有球移至 3 号柱子,耗费 $m - t_1$ 步。
  • 第五步:将 1 号柱子顶部的所有 2 号球移至 2 号柱子,耗费 $m - t_1$ 步。
  • 第六步:将 3 号柱子里所有 1 号球移至 1 号柱子,所有 2 号球移至 2 号柱子。耗费 $m$ 步。
  • 总计耗费 $5m - t_1$ 步。

扩展到 $n$ 种球的情况,考虑进行分治处理:定义函数 solve(l, r) 为保证 $l$ 至 $r$ 号柱中只含有 $l$ 至 $r$ 号球时的复原过程。

为了将 solve(l, r) 退化至两种球的情况,不难想到令 $mid = \frac{l + r}{2}$,若颜色 $x > mid$,则令其为 2 号球,反之为 1 号球。

具体实现时,需要以 $mid$ 为界,两端分别枚举未被还原的柱子。注意确定好需要还原的柱子以确保有柱子能在这轮操作中被还原,且第六步中需要稍作改动。这些细节留给读者。

期望步数为 $5mn \log n$。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
#include <bits/stdc++.h>
#define LL long long
#define ULL unsigned long long
#define LD long double
#define reg register
#define _rep(i, x, y) for(int i = x; i <= y; i++)
#define _per(i, x, y) for(int i = x; i >= y; i--)
template <typename T>
inline void read(T &x) {
x = 0; T f = (T) 1;
char c = getchar();
for(; !isdigit(c); c = getchar()) {if(c == '-') f = -f;}
for(; isdigit(c); c = getchar()) x = x * 10 + c - 48;
x *= f;
}
template <typename T, typename... Args>
inline void read(T& x, Args&... args) {
read(x); read(args...);
}
template <typename T>
inline void write(T x) {
if(x < 0) {x = -x; putchar('-');}
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
template <typename T>
inline void writesp(T x, char sp = ' ') {write(x); putchar(sp);}

const int maxN = 50 + 10, maxM = 400 + 10, maxMove = 8.2e5 + 10;
int n, m, a[maxN][maxM], f[maxN], stk[maxN], ans[maxMove][2], tot = 0;

void Move(int A, int B, int k) {
ans[++tot][0] = A; ans[tot][1] = B;
assert(stk[B] != m); assert(stk[A] != 0);
a[B][++stk[B]] = a[A][stk[A]--];
}

void solve(int l, int r) {
if(l == r) return;
int mid = (l + r) >> 1; memset(f, 0, sizeof(f));
_rep(i, l, mid) {
_rep(j, mid + 1, r) {
if(f[i] || f[j]) continue;
int Cnt = 0;
_rep(k, 1, m) Cnt += (a[i][k] <= mid) + (a[j][k] <= mid);
if(Cnt < m) { // recover column j
Cnt = 0;
_rep(k, 1, m) Cnt += (a[j][k] > mid);
// Step 1; Cnt moves;
_rep(k, 1, Cnt) Move(i, n + 1, 1);
// Step 2: M moves;
_rep(k, 1, m) Move(j, (a[j][stk[j]] > mid ? i : n + 1), 2);
// Step 3 (1) : M moves
_rep(k, 1, Cnt) Move(i, j, 3);
_rep(k, 1, m - Cnt) Move(n + 1, j, 4);
_rep(k, 1, m - Cnt) Move(i, n + 1, 5);
// Step 4
_rep(k, 1, m - Cnt) Move(j, i, 6);
// Step 5
while(stk[n + 1]) {
if(stk[j] != m && a[n + 1][stk[n + 1]] > mid) Move(n + 1, j, 7);
else Move(n + 1, i, 7);
}
f[j] = 1;
} else {
Cnt = 0;
_rep(k, 1, m) Cnt += (a[i][k] <= mid);
// Step 1; Cnt moves;
_rep(k, 1, Cnt) Move(j, n + 1, 1);
// Step 2: M moves;
_rep(k, 1, m) Move(i, (a[i][stk[i]] <= mid ? j : n + 1), 2);
// Step 3 (1) : M moves
_rep(k, 1, Cnt) Move(j, i, 3);
_rep(k, 1, m - Cnt) Move(n + 1, i, 4);
_rep(k, 1, m - Cnt) Move(j, n + 1, 5);
// Step 4
_rep(k, 1, m - Cnt) Move(i, j, 6);
// Step 5
while(stk[n + 1]) {
if(stk[i] != m && a[n + 1][stk[n + 1]] <= mid) Move(n + 1, i, 7);
else Move(n + 1, j, 7);
}
f[i] = 1;
}
}
}
solve(l, mid);
solve(mid + 1, r);
}
int main() {
read(n, m);
_rep(i, 1, n) {
_rep(j, 1, m) read(a[i][j]);
stk[i] = m;
}
solve(1, n);
writesp(tot, '\n');
_rep(i, 1, tot) printf("%d %d\n", ans[i][0], ans[i][1]);
return 0;
}