Description:
nn个人跑马拉松,起点固定重点随意,必须走最短路。按时间为第一关键字编号为第二关键字排名,分金银铜牌。金牌数量在之间,银牌数在[s1,s2][s1,s2]之间。问有多少种方案。
Solution:
考虑枚举金牌线和铜牌线,金牌线肯定出在每个人最快的路线,铜牌线出在最慢的路线。然后dp[i][j][k]dp[i][j][k]表示考虑第ii个人,有个金牌,kk个银牌的方案数,转移即可。然而这样会有大量不合法方案,因为可能没有人压线,那么考虑容斥,计算金牌线至多是多少和铜牌线至少是多少的方案,用容斥计算即可。
#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;
}