【HNOI 2008 明明的烦恼】树的prufer编码

    技术2022-05-17  54

    有一种叫树的prufer编码的神奇东西能将一棵点带标号的树一一对应地映射成一个长度为N-2的序列,方法如下:

    每次取出未被删除的标号最小的度为1的点,将这个点相邻的那个点加入序列,同时删除这个点,直到整棵树只剩两个点

    还原的话,只要从前往后扫这个序列a,将a[i]与序列a中i之后没出现的标号最小的且没被标记过点相连,同时将那个点标记一下,直到最后剩两个没标记的点,将他们相连即可

    这个编码方法也顺便解释了n个点的树有n^(n-2)个

    回到这道题,一个点的度数就是它在a序列中出现次数+1,所以这个问题就很容易解决了

    已经确定的点就用排列先排好,没确定的点就可以在剩下的位置里随便填

    最后要高精度

    代码:

    program syj; var n,i,j,k:longint; s:string; sh,a,ne,b,c,d:array[0..1005]of longint; procedure work(p,z:longint); var i:longint; begin fillchar(d,sizeof(d),0); for i:=p downto 2 do begin inc(d[i]); inc(c[sh[i]],d[i]*z); inc(d[ne[i]],d[i]); end; end; procedure no; begin writeln(0); close(input);close(output); halt; end; procedure cheng(k:longint); var i,x,o:longint; begin x:=0; for i:=1 to a[0] do begin o:=a[i]*k+x; a[i]:=o mod 10000; x:=o div 10000; end; while x>0 do begin inc(a[0]);a[a[0]]:=x mod 10000; x:=x div 10000; end; end; begin assign(input,'input.txt');reset(input); assign(output,'output.txt');rewrite(output); readln(n); for i:=2 to n do if sh[i]=0 then for j:=1 to n div i do begin sh[i*j]:=i; ne[i*j]:=j; end; j:=0; for i:=1 to n do begin read(a[i]); if a[i]=0 then no; if a[i]>-1 then begin inc(k,a[i]-1);work(a[i]-1,-1); end else inc(j); end; if (k>n-2)or(j=0)and(k<n-2) then no; work(n-2,1); work(n-2-k,-1); while j>1 do begin inc(c[sh[j]],n-k-2); j:=ne[j]; end; fillchar(a,sizeof(a),0); a[0]:=1;a[1]:=1; for i:=2 to n do for j:=1 to c[i] do cheng(i); write(a[a[0]]); for i:=a[0]-1 downto 1 do begin str(a[i],s); while length(s)<4 do s:='0'+s; write(s); end; writeln; close(input);close(output); end. 


    最新回复(0)