BZOJ1298:[SCOI2009]骰子的学问
Description
Input
第一行为两个整数n, m。第二行有n个整数,为a1,a2, …, an。
Output
包含n行,每行m个1~n×m的正整数,各不相同,以空格分开。如果有多解,输出任意一组解;如果无解,输出一个整数0。
Sample Input & Sample Output
HINT
30%的数据满足n, m≤10
100%的数据满足3≤n, m≤200
题解:
把ai认为是i的父亲,使其连边,那么题目给出的关系构成了一个基环树森林。
对于在环外、指向环的边(即“树”的部分),使骰子的每一个面都比其父亲大。
观察1、4样例可以发现一个构造方式:对于一个大小为n环,从第一个点开始,逆着父亲边放入1~n;再从第一个点在环上的儿子开始,逆着父亲边以此放入n+1~2*n,直到放满n*m个数。
但是这个构造法在样例3中失败了。事实上,可以证明,只有在像样例3这种环大小为3,骰子面数为4的情况下,该方法会失效。对于这种情况,进行特判。
注意环大小为2或是m<=2的情况,一定无法构造。
代码:
1 const 2 bb:array[1..12]of longint=(1,2,4,3,7,5,10,8,6,11,9,12); 3 var 4 i,j,k,l,n,m,cnt:longint; 5 fa,a,b,c:array[0..1001]of longint; 6 d:array[0..1001,0..1001]of longint; 7 procedure ss2(x:longint); 8 var i:longint; 9 begin 10 if a[x]<2 then 11 begin 12 for i:=1 to m do 13 begin inc(cnt); d[x,i]:=cnt; end; 14 a[x]:=2; 15 end; 16 i:=c[x]; 17 while i>0 do 18 begin 19 if a[i]<2 then ss2(i); 20 i:=b[i]; 21 end; 22 end; 23 procedure ss(x:longint); 24 var i,j,k,l,xx:longint; 25 begin 26 a[x]:=1; x:=fa[x]; 27 while a[x]=0 do 28 begin 29 a[x]:=1; x:=fa[x]; 30 end; 31 k:=1; xx:=fa[x]; a[x]:=2; 32 while x<>xx do begin inc(k); a[xx]:=2; xx:=fa[xx]; end; 33 if k<=2 then begin writeln(0); halt; end; 34 if(k=3)and(m=4)then 35 begin 36 for i:=1 to 12 do 37 begin 38 d[x,1+((i-1)div 3)]:=bb[i]; 39 x:=fa[x]; 40 end; 41 end else 42 begin 43 for i:=1 to m do 44 begin 45 l:=cnt+k; 46 for j:=1 to k do 47 begin 48 d[x,i]:=l; dec(l); 49 if j<>k then x:=fa[x]; 50 end; 51 cnt:=cnt+k; 52 end; 53 end; 54 for i:=1 to k do 55 begin 56 ss2(x); x:=fa[x]; 57 end; 58 end; 59 begin 60 readln(n,m); 61 for i:=1 to n do 62 begin 63 read(fa[i]); 64 b[i]:=c[fa[i]]; c[fa[i]]:=i; 65 end; 66 for i:=1 to n do 67 if a[i]=0 then ss(i); 68 for i:=1 to n do 69 begin 70 for j:=1 to m do write(d[i,j],' '); 71 writeln; 72 end; 73 end.