CTU open Contest 2016 Tree Stands

来源:互联网 发布:python配置opencv3.0 编辑:程序博客网 时间:2024/06/05 10:51
学校省赛选拔赛的题目
题目大意:给定一棵树,求问猎人在树上站法的合法方案,树有n个结点,m个猎人
tip1:每一个合法的站点至少有一个与其连通的节点上有人站 
tip2:一棵树最多站一个人
tip3:人之间不做区分
Tree Standshuntsmen.c, huntsmen.cpp, huntsmen.c11, Huntsmen.java, huntsmen.pyTree stands are elevated wooden platforms attached to trees. Typically, huntsmen use tree stands to watch or to shoot their prey.In our county, huntsmen have built a remarkable system of tree stands. The tree stands are connected by narrow straight paths which form a kind of maze on the hunting grounds. The builders wanted to minimize the impact on the environment and so they built the minimum possible number of paths which ensure that there is a connection between any two tree stands. Also, a tree stand is visible from another stand if and only if the two are connected by a path.A group of local huntsmen wants to find out which particular tree stands will serve the best their hunting interests. Each day they climb a different set of tree stands and watch the wildlife.There are a few more important circumstances to consider:• Security rules dictate that any occupied tree stand must be visible from at least one other occupied tree stand so that in case of an emergency the huntsman in the neighbour tree stand can come to help the colleague.• A tree stand is always occupied by at most one huntsman.• It does not matter which huntsman is in which tree stand. It only matters which tree stands are occupied and which are not.• The size of the group does not change.How many days will the group spend in the tree stands before they investigate all possible choices of tree stands available to them?Input SpecificationThere are more test cases. Each case starts with a line containing two integers N and K (2 ≤ K ≤ N ≤ 200) separated by space. N is the number of tree stands, K is the size of the group of huntsmen. The tree stands are labeled 1,2,3,... ,N.Next, there are N − 1 lines, each line specifies one path between two tree stands. The line contains the labels of the stands separated by a space. The order of the labels on a line and the order of the paths in the input is arbitrary.Output SpecificationFor each test case, print on a separate line the number of days which the group will spend in the tree stands. Express the result modulo 1000000007.Sample Input4 3 1 2 1 4 1 3 5 4 1 2 2 3 3 4 4 5Output for Sample Input3 3
#include <iostream>#include <cstdio>#include <cstring>#define no_stand 0#define safe 1#define notSafe 2using namespace std;typedef long long llt;const int N = 201;const int MOD = 1e9+7;int tree[N][N],treeCnt[N];//treeCnt[N]记录子树的个数int n,m;int dp[N][N][3],tmp[N][N][3];//dp[i][j][k]数组代表以i为根的树上站了j个人且树根i的状态为k的方案数//tmp[i][j][k]用于计算dp数组的辅助数组,对每一个根节点,代表前i个子树分配了j个人,且状态和为k的方案//这里指的状态和是指全部子树合在一起的状态,而且在搜索中"此时"根节点u不站人//状态和解释:no_stand状态是指正在搜索的结点的前i个子树的根节点都不站人//            safe状态是指正在搜索的结点的前i个子树没有不安全的结点,而且至少有一个处于safe状态的子节点//            Notsafe状态是指......至少有一个处于Notsafe的结点//关于三种状态://0:代表树根上不站人的合法状态//1:代表树根上站了的人的安全状态//2:代表树根上站了人的不安全状态,但是它的所有子树都安全(这里指子树的根节点都处于no_stand状态),这个状态可以通过在该节点的父亲上放个人来变为合法状态void dfs( int now ){    int sz = treeCnt[now];    for( int i=0;i<sz;i++ )        dfs( tree[now][i] );    memset( tmp,0,3*N*N*sizeof(int) );    //前0个子树站了一个0个节点而且全部没有站人(因为根本没有子树)    tmp[0][0][0] = 1LL;//对前i个子树    for( int i=1;i<=sz;i++ )    {//第i个子树个根为v        int v = tree[now][i-1];//给前i个子树分配j个人        for( int j=0;j<=m;j++ )        {//给前i-1个子树分配k个人,那么第i个子树就分配j-k个人            for( int k=0;k<=j;k++ )            {//前i个子树树根不站人:前i-1个子树树根不站人,且第i个子树树根不站人                tmp[i][j][0] += ((llt)tmp[i-1][k][0]*dp[v][j-k][0])%MOD;                tmp[i][j][0] %= MOD;//前i个子树有>=1个子树站了人的合法状态:前i-1个子树树根不站人,第i个子树安全                tmp[i][j][1] += ((llt)tmp[i-1][k][0]*dp[v][j-k][1])%MOD;                tmp[i][j][1] %= MOD;//前i个子树有>=1个子树站了人的合法状态:前i-1个子树安全,第i个子树树根不站人                tmp[i][j][1] += ((llt)tmp[i-1][k][1]*dp[v][j-k][0])%MOD;                tmp[i][j][1] %= MOD;//前i个子树有>=1个子树站了人的合法状态:前i-1个子树安全,第i个子树树根站人且安全                tmp[i][j][1] += ((llt)tmp[i-1][k][1]*dp[v][j-k][1])%MOD;                tmp[i][j][1] %= MOD;//前i个子树树根站人且树根上的人不安全:枚举一下,前i-1个子树或者第i个子树有一个不安全即为不安全状态,所以有5种                tmp[i][j][2] += ((llt)tmp[i-1][k][0]*dp[v][j-k][2])%MOD;                tmp[i][j][2] %= MOD;                tmp[i][j][2] += ((llt)tmp[i-1][k][2]*dp[v][j-k][0])%MOD;                tmp[i][j][2] %= MOD;                tmp[i][j][2] += ((llt)tmp[i-1][k][1]*dp[v][j-k][2])%MOD;                tmp[i][j][2] %= MOD;                tmp[i][j][2] += ((llt)tmp[i-1][k][2]*dp[v][j-k][1])%MOD;                tmp[i][j][2] %= MOD;                tmp[i][j][2] += ((llt)tmp[i-1][k][2]*dp[v][j-k][2])%MOD;                tmp[i][j][2] %= MOD;            }        }    }    //对于每一个结点为根的结点站了0个人的方法总数为1    dp[now][0][0] = 1;    for( int i=1;i<=m;i++ )    {//now节点不站人且安全:所有子树都不站人+所有子树都安全        dp[now][i][0] = (tmp[sz][i][0]+tmp[sz][i][1])%MOD;//now结点站人且安全:所有子树都安全+(子树为不安全状态,但只有子树树根不安全,可以转化为安全状态)        dp[now][i][1] = (tmp[sz][i-1][1]+tmp[sz][i-1][2])%MOD;//now节点站人且不安全:所有子树都不站人        dp[now][i][2] = tmp[sz][i-1][0];    }}//通过搜索,删除所有指向父亲的边,这样每个节点边的数量就等于它子树的数量void cutEdge( int now,int fa ){    for( int i=0;i<treeCnt[now]; )    {        int v = tree[now][i];        if( v==fa )             tree[now][i] = tree[now][ --treeCnt[now] ];        else        {             cutEdge( v,now );             i++;        }    }}void solve(){    memset( dp,0,sizeof(dp) );    //先切除多余的边,形成一棵树    cutEdge( 1,-1 );    dfs(1);    //因为是从第一棵子树开始dfs的,所以答案是以第一棵树为根节点,有m个stands的合法状态之和    printf("%d\n",(dp[1][m][1]+dp[1][m][0])%MOD);}int main(){//    freopen("in.in","r",stdin);//    freopen("o.o","w",stdout);    while( scanf("%d%d",&n,&m) != EOF )    {        memset( treeCnt,0,sizeof(treeCnt) );        for( int i=0;i<n-1;i++ )        {            int u,v;            scanf("%d%d",&u,&v);//由于输入不是以父亲-儿子的顺序输入的,所以不知道树根,建立双向边,不然跑不通//后面还需要删边,见cutEdge函数            tree[u][ treeCnt[u]++ ] = v;            tree[v][ treeCnt[v]++ ] = u;        }        solve();    }    return 0;}

原创粉丝点击