【CF53E】 Dead Ends

题目

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

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

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

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

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

输入格式:
第一行有三个整数nn,mm和kk(3\leq n\leq10 , n-1\leq m\leq \frac {n(n-1)} {2} , 2\leq k\leq n-13≤n≤10,n−1≤m≤
2
n(n−1)
​ ,2≤k≤n−1),依次代表了节点、道路和终点的数量。随后mm行,每行包含两个不同的整数v_1v
1
​ 和v_2v
2
​ (1\leq v_1 , v_2\leq n , v_1 \not= v_21≤v
1
​ ,v
2
​ ≤n,v
1
​ ̸=v
2
​ ),代表一条道路连接的两个节点。每一对节点之间最多有一条道路。节点被编号为1到nn。保证初始的图联通。

输出格式:
一个整数:满足条件的方案数。

题目描述
Life in Bertown has become hard. The city has too many roads and the government spends too much to maintain them. There are n n junctions and m m 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-1 n−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 k k 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-1 n−1 roads left.
It is possible to get from each junction to any other one.
There are exactly k k 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 n n , m m and k k ( 3<=n<=10,n-1<=m<=n·(n-1)/2,2<=k<=n-1 3<=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 m m lines each containing two different integers v_{1} v
1
​ and v_{2} v
2
​ ( 1<=v_{1},v_{2}<=n,v_{1}≠v_{2} 1<=v
1
​ ,v
2
​ <=n,v
1
​ ≠v
2
​ ) 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 1 1 to n n . 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.

输入输出样例
输入样例#1:
3 3 2
1 2
2 3
1 3
输出样例#1:
3
输入样例#2:
4 6 2
1 2
2 3
3 4
4 1
1 3
2 4
输出样例#2:
12
输入样例#3:
4 6 3
1 2
2 3
3 4
4 1
1 3
2 4
输出样例#3:
4

思路

题意在评论区有,这里就不赘述了。

这题妙啊,但我也不是太懂,看别人的方法学习了一波。

首先必定考虑DP,其次观察数据大小考虑状压DP,再看到答案求方案数考虑容斥。

DP数组f[i][j]表示当前已经考虑到i个点,j个点为死胡同,答案即为

ans=\sum f[(1<<n)-1][i]
由于图很小,使用邻接矩阵g[i][j]存图。

关于方程就判定生成是否合法即可(所以跟容斥有毛关系)。

代码

#include<cstdio>
#include<algorithm>
#define li long long
#define gc getchar()
#define pc putchar
inline li read(){
    li x = 0,y = 0,c = gc;
    while(!isdigit(c)) y = c,c = gc;
    while(isdigit(c)) x = x * 10 + (c ^ '0'),c = gc;
    return y == '-' ? -x : x;
}
inline void print(li x){
    if(x < 0) pc('-'),x = -x;
    if(x >= 10) print(x / 10);
    pc(x % 10 + '0');
}
int n,m,p,ed[20][20],wei[150010],mx,dy[20];
li jz[20][20],as[150010];
const int mo = 1000000007;
inline li ksm(li q,li w){
    li as = 1;
    while(w){
        if(w & 1) as = as * q % mo;
        q = q * q % mo;
        w >>= 1;
    }
    return as;
} 
inline li wk(int cnt){
    register int i,j,k;
    li as = 1,tmp;
    for(i = 1;i <= cnt;++i){
        if(!jz[i][i]){
            int fg = 0;
            for(j = i + 1;j <= cnt;++j) if(jz[j][i]){
                fg = j;break;
            }
            if(!fg) return 0;
            for(j = i;j <= cnt;++j) std::swap(jz[i][j],jz[fg][j]);
        }
        (as *= jz[i][i]) %= mo;
        tmp = ksm(jz[i][i],mo - 2);
        for(j = i;j <= cnt;++j) (jz[i][j] *= tmp) %= mo;
        for(j = i + 1;j <= cnt;++j){
            for(k = cnt;k >= i;--k) (jz[j][k] += mo - (jz[i][k] * jz[j][i]) % mo) %= mo;
        }
    }
    return as;
}
int main(){
    int i,j,k,l,u,v;
    n = read();m = read();p = read();
    for(i = 1;i <= m;++i){
        u = read();v = read();ed[u][v] = ed[v][u] = 1;
    }
    mx = 1 << n;
    for(i = 0;i < mx;++i){
        wei[i] = wei[i >> 1] + (i & 1);
        for(j = 1;j <= n;++j) for(k = 1;k <= n;++k) jz[j][k] = 0;
        int cnt = 0;
        for(j = 1;j <= n;++j) if(!(i & (1 << j - 1))) dy[++cnt] = j;
        for(j = 1;j < cnt;++j) for(k = j + 1;k <= cnt;++k) if(ed[dy[j]][dy[k]]) jz[j][k] = jz[k][j] = mo - 1,++jz[j][j],++jz[k][k];
        as[i] = wk(cnt - 1);
        for(j = 1;j <= n;++j) if(i & (1 << j - 1)){
            int tmp = 0;
            for(k = 1;k <= n;++k) if(ed[j][k] && !(i & (1 << k - 1))) ++tmp;
            as[i] *= tmp;
        }
    }
    for(i = 1;i < mx;i <<= 1) for(j = 0;j < mx;j += (i << 1)) for(k = j;k < j + i;++k) as[k] -= as[k + i];
    li ans = 0;
    for(i = 0;i < mx;++i) if(wei[i] == p) ans += as[i];
    print(ans);  
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值