hdu5779 Tower Defence

题意:
求满足【n个点编号1-n,从1出发到各个点的最短路长度不等于k】的图有多少种,所有边长都为1,没有重边和自环,不连通则距离为无穷大

思路

题目中“不等于k”可以换成“小于k或者无穷大”。为了将小于k和无穷大分开,将所有点分为两类,第一类与1连通,第二类与1不连通,即为无穷大。分类后,第二类点和第一类点之间没有边,第二类点相互之间的边怎样连接也没有限制,问题简化为求第一类点中满足条件的方案数。
f[i][j][k] 表示:包含1的i个点的连通块中、从1出发到所有点的最短路的距离中、有j个距离恰好为k的方案数。
确定新状态: f[i+x][x][k+1]
x个点内部连边方案数为 2x(x1)/2 ;x个点中每个点向j个最短路距离为k的点连边,可以任意连但不能一条都不连,所以方法有 2j1 ,所以x个点共有 (2j1)x 种; i+x 个点中需要确定这x个点,所以这里有 Cxi+x 种。
因此建立转移方程: f[i][j][k] * 2x(x1)/2 * (2j1)x * Cxi+x —> f[i+x][x][k+1]
回到原问题,先将n个点分成两类,假定第一类有i个点,且其中包含1,确定这 i1 个点方案数有 Ci1n1 ,第二类点则有 ni 个,内部互连方案数为 2(ni)(ni1)/2 ,第一类点是个连通块,答案显然可以由f数组得到,即 f[i][1..i][1..k1] ,因此最后答案为 f[i][1..i][0..k1] * Ci1n1 * 2(ni)(ni1)/2
离线预处理复杂度为 O(n4)

#include <bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;
const int N = 62;
typedef long long ll;
ll fac[N], facinv[N], f[N][N][N], ans;
int n, K, T;
ll powermod(ll a, ll n, ll mod) {
    ll ans = 1;
    while (n) {
        if (n & 1) ans = ans * a % mod;
        a = a * a % mod;
        n >>= 1;
    }
    return ans;
}

ll C(ll n, ll m, ll mod) {
    return fac[n] * facinv[n - m] % mod * facinv[m] % mod;
}
ll F(int n) {
    ll ans = 0;
    for (int k = 0; k < K; k++) {
        for (int j = 1; j <= n; j++) {
            ans = (ans + f[n][j][k]) % mod;
        }
    }
    return ans;
}
void init() {
    fac[0] = facinv[0] = 1;
    for (int i = 1; i < N; i++) {
        fac[i] = fac[i - 1] * i % mod;
        facinv[i] = facinv[i - 1] * powermod(i, mod - 2, mod) % mod;
    }
    f[1][1][0] = 1;
    for (int i = 1; i < N; i++) {
        for (int j = 1; j <= i; j++) {
            for (int k = 0; k < i; k++) {
                for (int x = 1; i + x < N; x++) {
                    f[i + x][x][k + 1] = (f[i + x][x][k + 1] + f[i][j][k] * powermod(powermod(2, j, mod) - 1, x, mod) % mod *
                                          powermod(2, x * (x - 1) / 2, mod) % mod * C(i + x - 1, x, mod) % mod) % mod;
                }
            }
        }
    }
}

int main() {
    //freopen("in.txt", "r", stdin);
    init();
    cin >> T;
    while (T--) {
        cin >> n >> K;
        ans = 0;
        for (int i = 1; i <= n; i++) {
            ans = (ans + powermod(2, (n - i) * (n - i - 1) / 2, mod) * F(i) % mod * C(n - 1, i - 1, mod) % mod) % mod;
        }
        cout << ans << endl;
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值