[jzoj]1794. 保镖排队(树形DP+想法)

来源:互联网 发布:网络成瘾调查问卷 编辑:程序博客网 时间:2024/05/01 14:02

https://jzoj.net/senior/#contest/show/2013/2

Problem

给定n个人的“上司“,求出一个排列,使得每个人的上司在其前面,且一个人的下司一样按照等级顺序排好.

Data constraint

对于20%的数据,有N ≤ 9;
对于40%的数据,有对于所有K,有K ≤ 2;
对于60%的数据,有N ≤ 100;
对于100%的数据,有T ≤ 10,N ≤ 1000,K ≤ N。

Solution


【重要性质】

按照所属关系建立好后,发现这是一棵树.


【基本概念】

假设,当前一个父亲x,它有n个儿子,分别为x1,x2xn.

因为这n个儿子必须是有序的,当这n个儿子都没有儿子时,那么总的方案数就是1.

而当有一个儿子有儿子时,我们就可以拿x的儿子去与其构成不同的排列,方案数就会增加.

那么怎么算才不会把方案数算重?


【状态设置】

g[i]表示以i为根的子树所有合法的方案数.


【更新状态】

我们可以这么想,如果一个父亲x,它的儿子x1xn全部的答案都已经算好了,如何更新g[x]

假设现在算xi对于x的贡献,那么很显然有

g[x]=g[x]g[xi]
进一步思考可以发现,可以把xi级别小的所有节点都插入到xi的子树中,那么这里就是求i个数插进j个空里可以用组合去求,实际上组合数是一个
f[i,j]=f[i,j1]+f[i1,j]

所以就有了方程:

g[x]=g[x]f[tot[xi],toter[next[xi]]+1]

解释一下,tot[x]表示以x为根的子树大小,toter[x]表示级别比x小但是同一个父亲的所有子树大小的和.

最后加个1实际上是组合数的运用,这个可以自己推推.

虽然这个组合数很好码,但是要发现并不简单,其实可以用普通的排列组合先试一下,随后再推一推这个式子

那么这道题就可以很完美解决了.


var        a,f:array[0..1000,0..1000] of int64;        t,tot,toter,next,tov,last,g,nexter:array[0..10000] of int64;        tt,w,n,i,j,len,node:longint;procedure insert(x,y:longint);begin        inc(len); tov[len]:=y; next[len]:=last[x]; last[x]:=len;end;procedure dfs(k:longint);var        x:longint;begin        tot[k]:=1;        x:=last[k];        while x>0 do        begin                dfs(tov[x]);                tot[k]:=(tot[k]+tot[tov[x]]) mod 10007;                x:=next[x];        end;end;procedure dp(k:longint);var        x:longint;begin        g[k]:=1;        x:=last[k];        while x>0 do        begin                dp(tov[x]);                g[k]:=(g[k]*g[tov[x]]) mod 10007;                if nexter[tov[x]]>0 then g[k]:=(g[k]*f[ tot[tov[x]] , toter[nexter[tov[x]]]+1 ]) mod 10007;                x:=next[x];        end;end;begin        readln(tt);        for w:=1 to tt do        begin                fillchar(nexter,sizeof(nexter),0);                fillchar(last,sizeof(last),0);                fillchar(tot,sizeof(tot),0);                fillchar(toter,sizeof(toter),0);                readln(n);                len:=0;                for i:=1 to n do                begin                        read(t[i]);                        for j:=1 to t[i] do                        begin                                read(a[i,j]);                                insert(i,a[i,j]);                                if j>1 then nexter[a[i,j-1]]:=a[i,j];                        end;                end;                f[1,1]:=1;                for i:=1 to n do                        for j:=1 to n do                                if i+j>2 then f[i,j]:=(f[i,j-1]+f[i-1,j]) mod 10007;                dfs(1);                for i:=1 to n do                begin                        toter[a[i,t[i]]]:=tot[a[i,t[i]]];                        for j:=t[i]-1 downto 1 do                                toter[a[i,j]]:=(tot[a[i,j]]+toter[a[i,j+1]]) mod 10007;                end;                node:=0;                dp(1);                writeln(g[1]);        end;end.