保镖排队
(p3.pas/cpp/in/out)
【问题背景】
教主LHX作为知名人物,时刻会有恐怖分子威胁他的生命。于是教主雇佣了一些保镖来保障他的人生安全。
【题目描述】
教主一共雇佣了N个保镖,编号为1~N。每个保镖虽然身手敏捷武功高强,但是他在其余N-1个保镖里,都会有一个“上司”,他会对他的上司言听计从。但一号保镖例外,他武功盖世,不惧怕其余任何保镖,所以他没有上司。
教主LHX会对这N个保镖进行定期视察。每次视察的时候,首先会让所有保镖排队。
对于每个保镖,在他心目中会对他的所有下属的武功实力排个队。
现在教主要求排出来的队伍满足:①互为上司-下属的两个保镖,上司在前,下属在后②对于一个保镖的所有下属,武功实力较强的在前,较弱的在后。
教主想知道,总的排队方法数除以10007的余数是多少。
【输入格式】
输入的第一行为一个正整数T,表示了数据组数。
对于每组数据:
第一行为一个正整数N。
接下来N行,每行描述一个保镖。
第i+1行,会有一个整数K,代表第i个保镖的下属个数,接下来K个数,代表第i个保镖的下属按照武功实力从高到低的编号。
【输出格式】
输出包括C行,每行对于每组数据输出方案数mod 10007后的结果。
【样例输入】
2
5
2 2 3
2 4 5
0
0
0
7
2 2 3
2 4 5
2 6 7
0
0
0
0
【样例输出】
3
10
【样例说明】
对于第1组数据,有以下3种排列是合法的:
1 2 4 3 5
1 2 3 4 5
1 2 4 5 3
同时满足了1在2与3之前且2在3之前,2在4与5之前且4在5之前
【数据规模】
对于20%的数据,有N ≤9;
对于40%的数据,有对于所有K,有K ≤2;
对于60%的数据,有N ≤100;
对于100%的数据,有T ≤10,N ≤1000,K ≤N。
【考察点】
树形DP
【思路】
看题目,给了若干的从属关系……所以很明显跟树有关了。
先把题目中的森林按照“左儿子,右兄弟”的原则(或者其他的什么原则)转化成一棵二叉树。我们用F[i]表示以i为根的子树的排列方案树,S[i]表示这棵子树的规模,经过一系列各种蛋疼的推导,我们可以找到转移方程: f[x]=f[L[x]]*f[R[x]]*C(s[L[x]]+s[R[x]]-1,s[L[x]]-1),其中C就是组合数呐、
然后就是万年不变的树形DP模式了……
话说……我做题的时候完全没有意识到那货是个组合数……我是观察+乱搞得出的……
【提交情况】
1次AC
【经验】
那什么……多叉转二叉其实不一定要搞出一棵二叉树,就像线段树一样,心中有树就行了……
【收获】
我觉得搞完这道题……我的代码调试能力又有提升了……
【吐槽】
我怀疑我把Lazarus编译器调蹦了……上述转移方程的某一步计算总是跟手动不一样。搞了很久怒换C++,翻译一次果然就行了……虽然各种后续问题出现,又调了半天= =
还有就是……转二叉还是用标准方法好……奇葩方法很容易在调试的时候把人搅晕……
ACCode:
#include
#include
#include
//#include
using namespace std;
int N;
int c[2500][2500];
int map[2500][2500];
int s[2500],f[2500];
void init()
{
freopen("p3.in","r",stdin);
freopen("p3.out","w",stdout);
c[0][0]=1;
for(int i=1;i<2500;++i)
{
c[i][0]=c[i][i]=1;
for(int j=1;j
c[i][j]=(c[i-1][j-1]+c[i-1][j])%10007;
}
//翻译过来的时候忘掉了……怪不得半天没反应- -
//for (int i=0;i<=20;i++)
//{
//for (int j=0;j<=20;j++)
//cout<
//cout<
//}
}
void readdata()
{
scanf("%d",&N);
for(int i=1;i<=N;++i)
{
scanf("%d",&map[i][0]);
for(int j=1;j<=map[i][0];j++)
scanf("%d",&map[i][j]);
}
}
void solve(int x) //递归没什么要说的……
{
intt;
f[x]=1;
s[x]=0;
for(int i=map[x][0];i>=1;i--)
{
t=map[x][i];
//cout<
solve(t);
f[x]=(((f[x]*f[t])%10007)*(c[s[t]+s[x]-1][s[t]-1]))%10007; //Mod10007这种问题嘛……还是每个小运算都Mod一下,安全起见;这货就是转移方程了……
//cout<<"f["<
s[x]+=s[t];
//cout<
}
s[x]=s[x]+1; //记住维护S数组
}
int main()
{
intt;
init();
scanf("%d",&t);
for(int i=1; i<=t; i++)
{
readdata();
solve(1); //递归处理
//for (int i=1; i<=N; i++) cout<
//cout<
printf("%d\n",f[1]);
}
return 0;
}