1 条题解

  • 0
    @ 2026-5-10 0:52:37

    简单构造。

    图建出来是基环树森林。树的部分直接造必胜骰子即可,只要考虑环的情况。下文默认图是一个环,方便起见在反图上构造必败的情况。

    首先 n2n\le 2 的情况下必定无解。以 n=4,m=4n=4,m=4 为例,观察样例构造:

    $$\begin{matrix} 1&8&11&14\\ 2&5&12&15\\ 3&6&9&16\\ 4&7&10&13\\ \end{matrix}$$

    这给我们一个重要思路:一列一列构造,保证后面的列中的数比前面大。这样在相邻两个骰子的比较中,我们只关心对应位置的数的大小关系了。

    将其转化为 0101 矩阵,设 bi,j=[ai1,j<ai,j]b_{i,j}=[a_{i-1,j}<a_{i,j}],其中 a0=ana_0=a_n。则能得到:

    $$\begin{matrix} 0&1&1&1\\ 1&0&1&1\\ 1&1&0&1\\ 1&1&1&0\\ \end{matrix}$$

    考虑一个 bb 合法的充要条件:

    1. 对于任意一行,11 的个数 >m/2>m/2,也即满足必败。
    2. 对于任意一列,其不能全 00 或全 11,也即可以构造一个合法的 aa

    发现限制极松。具体地,让每列有尽可能多的 11 是不劣的。因此让每列只有一个 00。按如下方式轮换构造即可:

    $$\begin{matrix} 0&1&1&1&0&1&1&1&0&1&\cdots\\ 1&0&1&1&1&0&1&1&1&0&\cdots\\ 1&1&0&1&1&1&0&1&1&1&\cdots\\ 1&1&1&0&1&1&1&0&1&1&\cdots\\ \end{matrix}$$

    在该构造下,唯一一个反例是 n=3,m=4n=3,m=4 的情况:

    $$\begin{matrix} \color{red}{0}&\color{red}{1}&\color{red}{1}&\color{red}{0}&\\ 1&0&1&1&\\ 1&1&0&1&\\ \end{matrix}$$

    至于为什么是唯一的反例?考虑反例的出现条件:存在一行 00 个数大于 11 个数。考虑到两个 00 间会间隔 n1n-111,只有 0 1 1 00\ 1\ 1\ 0 的情况会出现不合法。

    如何解决?事实上样例给出了一组 n=3,m=4n=3,m=4 的解,套用即可。时间复杂度 O(nm)\mathcal{O}(nm)

    #include <bits/stdc++.h>
    
    using namespace std;
    
    typedef long long ll;
    
    const int MAXN = 2e2 + 10;
    
    int n, m, a[MAXN], d[MAXN], ans[MAXN][MAXN], tot; vector<int> g[MAXN];
    
    void dfs(int u) { for (int v : g[u]) { for (int i = 0; i < m; i++) ans[v][i] = ++tot; dfs(v); } }
    
    int main() {
    	scanf("%d%d", &n, &m);
    	for (int i = 1; i <= n; i++) scanf("%d", &a[i]), d[a[i]]++;
    	queue<int> q;
    	for (int i = 1; i <= n; i++) if (!d[i]) q.emplace(i);
    	for (; !q.empty(); q.pop()) if (!--d[a[q.front()]]) q.emplace(a[q.front()]);
    	for (int i = 1; i <= n; i++) if (!d[i]) g[a[i]].emplace_back(i);
    	for (int i = 1; i <= n; i++) {
    		if (d[i] <= 0) continue; vector<int> tmp;
    		for (int u = i; d[u] > 0; tmp.emplace_back(u), d[u] = -1, u = a[u]);
    		if (tmp.size() <= 2) return puts("0"), 0;
    		if (tmp.size() == 3 && m == 4) {
    			ans[tmp[0]][0] = ++tot, ans[tmp[1]][0] = ++tot, ans[tmp[0]][1] = ++tot, ans[tmp[2]][0] = ++tot;
    			ans[tmp[2]][1] = ++tot, ans[tmp[2]][2] = ++tot, ans[tmp[1]][1] = ++tot, ans[tmp[1]][2] = ++tot;
    			ans[tmp[1]][3] = ++tot, ans[tmp[0]][2] = ++tot, ans[tmp[0]][3] = ++tot, ans[tmp[2]][3] = ++tot;
    			continue;
    		}
    		reverse(tmp.begin(), tmp.end());
    		for (int i = 0; i < m; i++) {
    			for (int j = 0; j < tmp.size(); j++) {
    				ans[tmp[(i + j) % tmp.size()]][i] = ++tot;
    			}
    		}
    	}
    	for (int i = 1; i <= n; i++) if (d[i] < 0) dfs(i);
    	for (int i = 1; i <= n; i++) {
    		for (int j = 0; j < m; j++) printf("%d ", ans[i][j]);
    	}
    }
    
    • 1

    信息

    ID
    2951
    时间
    1000ms
    内存
    128MiB
    难度
    10
    标签
    递交数
    4
    已通过
    1
    上传者