P2565 [SCOI2009]骰子的学问
P2565 [SCOI2009]骰子的学问
题面解析
题面 link : P2565
给定一个大小为
对于骰子
该题的全部样例如下(对解题有所帮助):
算法分析
将骰子按照骰子
此时整张图可以被拆分成为两个部分,分别是环内与环外。对于任意的处于环外的点,它应当保证所有的父亲均大于它。那么可以形成一种在环外的构造方法:
对于环外的每一个点,保证保证它的父亲的每一面的点数都比它的最大点数大。显然环外的骰子直接分配极大值就行了。
这样问题就缩小成了运用
而对于在环内的骰子。选择一个,向父亲方向依次放下
证明:
两个骰子可以等概率的投出
对于任意的
对于填在第
而对于任意的一个骰子,被选出的次数最多应该为
-
-
q = 3 & \\ m = 4 & \end{matrix}\right.
出现的不合法情况特判即可。
算法实现
为了方便实现可将原有环内操作修改为如下:
选择一个点,向父亲方向依次放下
这样显然与原有结论并不矛盾,同时又方便实践。
最后拓扑排序即可
code
#include <bits/stdc++.h>
const int SIZE = (int)2e2 + 50;
const int Sample[4][5] = {{},{0,1,3,10,11},{0,2,7,8,9},{0,4,5,6,12}};
int n,m,tot,head,tail;
int son[SIZE],indegree[SIZE],que[SIZE];
int ans[SIZE][SIZE];
inline void toposort()
{
for (int i = 1; i <= n; ++i) if (!indegree[i]) que[++tail] = i;
while (head <= tail)
{
int u = que[head++]; --indegree[son[u]];
for (int i = 1; i <= m; ++i) ans[u][i] = tot--;
if (!indegree[son[u]]) que[++tail] = son[u];
}
for (int i = 1; i <= n; ++i)
{
if (!indegree[i]) continue; tail = 0;
for (int u = i; indegree[u]; u = son[u]) indegree[que[++tail] = u] = 0;
if (tail < 3) {printf("0\n"); exit(0);}
tot -= tail * m;
if (tail == 3 && m == 4)
{
ans[que[1]][1] = Sample[1][1] + tot; ans[que[1]][2] = Sample[1][2] + tot;
ans[que[1]][3] = Sample[1][3] + tot; ans[que[1]][4] = Sample[1][4] + tot;
ans[que[2]][1] = Sample[2][1] + tot; ans[que[2]][2] = Sample[2][2] + tot;
ans[que[2]][3] = Sample[2][3] + tot; ans[que[2]][4] = Sample[2][4] + tot;
ans[que[3]][1] = Sample[3][1] + tot; ans[que[3]][2] = Sample[3][2] + tot;
ans[que[3]][3] = Sample[3][3] + tot; ans[que[3]][4] = Sample[3][4] + tot;
continue;
}
for (int j = 1,now = 1; now <= m; j = (j == 1 ? tail : j - 1), ++now)
{
ans[que[j]][now] = ++tot;
for (int k = (j == 1 ? tail : j - 1); k ^ j; k = (k == 1 ? tail : k - 1))
ans[que[k]][now] = ++tot;
}
tot -= tail * m;
}
}
signed main()
{
cin >> n >> m; tot = n * m; head = 1;
for (int i = 1; i <= n; ++i) cin >> son[i],indegree[son[i]]++;
for (int i = 1; i <= n; ++i) if (son[i] == i) return printf("0\n"),0;
toposort();
for (int i = 1; i <= n; ++i,printf("\n"))
for (int j = 1; j <= m; ++j)
printf("%d ",ans[i][j]);
return 0;
}