CF53E Dead Ends

CF53E Dead Ends

洛谷评测传送门

题目描述

Life in Bertown has become hard. The city has too many roads and the government spends too much to maintain them. There are nn junctions and mm two way roads, at which one can get from each junction to any other one. The mayor wants to close some roads so that the number of roads left totaled to n-1n−1 roads and it were still possible to get from each junction to any other one. Besides, the mayor is concerned with the number of dead ends which are the junctions from which only one road goes. There shouldn't be too many or too few junctions. Having discussed the problem, the mayor and his assistants decided that after the roads are closed, the road map should contain exactly kk dead ends. Your task is to count the number of different ways of closing the roads at which the following conditions are met:

  • There are exactly n-1n−1 roads left.
  • It is possible to get from each junction to any other one.
  • There are exactly kk dead ends on the resulting map.

Two ways are considered different if there is a road that is closed in the first way, and is open in the second one.

输入格式

The first line contains three integers nn , mm and kk ( 3<=n<=10,n-1<=m<=n·(n-1)/2,2<=k<=n-13<=n<=10,n−1<=m<=n⋅(n−1)/2,2<=k<=n−1 ) which represent the number of junctions, roads and dead ends correspondingly. Then follow mm lines each containing two different integers v_{1}v1 and v_{2}v2 ( 1<=v_{1},v_{2}<=n,v_{1}≠v_{2}1<=v1,v2<=n,v1�=v2 ) which represent the number of junctions connected by another road. There can be no more than one road between every pair of junctions. The junctions are numbered with integers from 11 to nn . It is guaranteed that it is possible to get from each junction to any other one along the original roads.

输出格式

Print a single number — the required number of ways.

题意翻译

题目描述:

Bertown的生活变得困难了起来。这个城市有太多的道路,而且政府花费了太多来维护这些道路。这里有nn个节点和mm条双向道路,且两两节点之间可以通过道路相互到达。现在市长想要关闭一些道路,使最后总共有n-1n−1条道路留下,并且所以节点之间仍然联通。另外,市长很关心终点,也就是只有一条道路可以到达的点的数量。终点不能太多也不能太少。在讨论过这个问题之后,市长和他的助手们觉得在应该关闭的道路关闭后,应该总共有恰好kk个终点。你的任务是求出满足以下三个条件的方案数:

1.有恰好n-1n−1条道路保留下来;

2.整张道路图仍然联通;

3.最后有恰好kk个终点在道路图上。

如果有一条道路在第一种方案中被关闭而在第二种方案中没有被关闭,那么我们认为这两种方案不同。

输入格式:

第一行有三个整数nn,mm和kk(3leq nleq10 , n-1leq mleq frac {n(n-1)} {2} , 2leq kleq n-13≤n≤10,n−1≤m≤2n(n−1),2≤kn−1),依次代表了节点、道路和终点的数量。随后mm行,每行包含两个不同的整数v_1v1和v_2v2(1leq v_1 , v_2leq n , v_1 ot= v_21≤v1,v2≤n,v1�=v2),代表一条道路连接的两个节点。每一对节点之间最多有一条道路。节点被编号为1到nn。保证初始的图联通。

输出格式:

一个整数:满足条件的方案数。

输入输出样例

输入 #1复制

输出 #1复制

输入 #2复制

输出 #2复制

输入 #3复制

输出 #3复制

题解:

2019.11.13CSP-S模拟赛满分场

写在前面

众所周知,NOIPCSP-S模拟赛是一种奇妙的赛事,千万不要被它的名字迷惑。它属于一种类型和性质未知的比赛。在这种奇妙的比赛里,200行的树链剖分非模板题可以被出题人当作(D1\,\,T1)的水题来使用。(T3)如果不用黑题,那么这场比赛就会被成群的大佬吐槽过水。为了扩宽选手们的眼界,这种赛事大量适当地添加了些许超过联赛范围的知识点。具体可能为后缀自动机,NTT算法,LCT等等......

总之,蒟蒻被一顿惨虐,在一个月的煎熬和瑟瑟发抖后,蒟蒻终于迎来了在考场上切掉的为数不多的几道题...

题解:

看这个数据范围,应该能想到状压DP,但是这个状态的设置比较伤脑筋。我们做状态压缩的时候很少见到过这种题目。但是根据动态规划的一般思路,我们可以确定动态规划状态的设置是基于问题边界的基础上的。于是我们仔细分析这道题的问题边界。假如我们要枚举的话,首先要枚举当前图所有的生成树,其次要枚举度为1的节点。那么就设置两维数组,第一维度存树的状态,第二维度存叶子节点的状态。

那么转移的时候会有两种更新的方式:第一种:把某个点加到一个叶子节点上,这个时候这个新节点就成为了叶子节点,叶子节点的数目是没有变化的。只是被替换了而已。第二种:把某个点加到非叶子节点上。这个时候新节点变成叶子节点,叶子节点的总数增加了1.

代码:

#include<cstdio>
#include<vector>
#define int long long
using namespace std;
const int maxs=(1<<11)+10;
vector<int> map[20];
int dp[maxs][maxs],cnt[maxs];
//dp[i][j]表示生成树状态为i,度数为1的点状态为j的方案数
int n,m,k,ans;
signed main()
{
    for(int i=1;i<=maxs;i++)
        for(int j=0;j<15;j++)
            if(i&(1<<j))
               cnt[i]++;
    scanf("%lld%lld%lld",&n,&m,&k);
    for(int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%lld%lld",&x,&y); 
        x--;y--;
        map[x].push_back(y);
        map[y].push_back(x);
    }
    for(int i=1;i<(1<<n);i<<=1) 
        dp[i][i]=1;
    for(int i=1;i<(1<<n);i++)
        for(int j=i;j;j--,j&=i)
            if(dp[i][j])
                for(int k=0;k<n;k++)
                    if(i&(1<<k))
                        for(int l=0;l<map[k].size();l++)
                        {
                            int to=map[k][l];
                            int now;
                            if(~i&(1<<to))
                            {
                                if(cnt[i]==1) 
                                    now=i|(1<<to);
                                else
                                    now=j&~(1<<k)|(1<<to);
                                if(!(now>>to+1)) 
                                    dp[i|(1<<to)][now]+=dp[i][j];
                            }
                        }
    for(int i=0;i<(1<<n);i++) 
        if(cnt[i]==k)
           ans+=dp[(1<<n)-1][i];
    printf("%lld",ans);
    return 0;
}
原文地址:https://www.cnblogs.com/fusiwei/p/11851987.html