Description:
n
n
个人跑马拉松,起点固定重点随意,必须走最短路。按时间为第一关键字编号为第二关键字排名,分金银铜牌。金牌数量在之间,银牌数在
[s1,s2]
[
s
1
,
s
2
]
之间。问有多少种方案。
Solution:
考虑枚举金牌线和铜牌线,金牌线肯定出在每个人最快的路线,铜牌线出在最慢的路线。然后
dp[i][j][k]
d
p
[
i
]
[
j
]
[
k
]
表示考虑第
i
i
个人,有个金牌,
k
k
<script type="math/tex" id="MathJax-Element-7">k</script>个银牌的方案数,转移即可。然而这样会有大量不合法方案,因为可能没有人压线,那么考虑容斥,计算金牌线至多是多少和铜牌线至少是多少的方案,用容斥计算即可。
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 55;
int n, m, g1, g2, s1, s2;
ll ans;
int mn[N], mx[N], d[N][N], s[N], g[N], b[N];
ll dp[N][N][N];
ll solve(int gl, int bl, int su, int sd) {
for(int i = 1; i <= n; ++i) {
g[i] = s[i] = b[i] = 0;
for(int j = 1; j <= n; ++j) {
if(i != j) {
if(d[i][j] <= gl) {
g[i] = 1;
}
if(d[i][j] >= bl) {
b[i] = 1;
}
if(d[i][j] > su && d[i][j] < sd) {
s[i] = 1;
}
}
}
}
memset(dp, 0, sizeof(dp));
dp[0][0][0] = 1;
for(int i = 1; i <= n; ++i) {
for(int j = 0; j <= i; ++j) {
for(int k = 0; k + j <= i; ++k) {
if(g[i] && j) {
dp[i][j][k] += dp[i - 1][j - 1][k];
}
if(s[i] && k) {
dp[i][j][k] += dp[i - 1][j][k - 1];
}
if(b[i] && j + k < i) {
dp[i][j][k] += dp[i - 1][j][k];
}
}
}
}
ll ret = 0;
for(int i = g1; i <= g2; ++i) {
for(int j = s1; j <= s2; ++j) {
ret += dp[n][i][j];
}
}
return ret;
}
int main() {
scanf("%d%d", &n, &m);
memset(d, 0x3f3f, sizeof(d));
for(int i = 1; i <= n; ++i) {
d[i][i] = 0;
}
for(int i = 1; i <= m; ++i) {
int u, v, w;
scanf("%d%d%d", &u, &v, &w);
d[u][v] = d[v][u] = w;
}
for(int k = 1; k <= n; ++k) {
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= n; ++j) {
d[i][j] = min(d[i][j], d[i][k] + d[k][j]);
}
}
}
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= n; ++j) {
if(d[i][j] < 0x3f3f3f3f) {
d[i][j] = d[i][j] * n + i;
}
}
}
scanf("%d%d%d%d", &g1, &g2, &s1, &s2);
for(int i = 1; i <= n; ++i) {
mx[i] = -0x3f3f3f3f;
mn[i] = 0x3f3f3f3f;
for(int j = 1; j <= n; ++j) {
if(i != j) {
mn[i] = min(mn[i], d[i][j]);
mx[i] = max(mx[i], d[i][j]);
}
}
}
for(int i = 1; i <= n; ++i) {
for(int j = 1; j <= n; ++j) {
ans += solve(mn[i], mx[j], mn[i], mx[j]) - solve(mn[i] - 1, mx[j], mn[i], mx[j]) - solve(mn[i], mx[j] + 1, mn[i], mx[j]) + solve(mn[i] - 1, mx[j] + 1, mn[i], mx[j]);
}
}
printf("%lld\n", ans);
return 0;
}