$ color{#0066ff}{ 题目描述 }$
Access Globe 最近正在玩一款战略游戏。在游戏中,他操控的角色是一名C 国士 兵。他的任务就是服从指挥官的指令参加战斗,并在战斗中取胜。
C 国即将向D 国发动一场秘密袭击。作战计划是这样的:选择D 国的s 个城市, 派出C 国战绩最高的s 个士兵分别秘密潜入这些城市。每个城市都有一个危险程度did_idi,
C 国指挥官会派遣战绩最高的士兵潜入所选择的城市中危险程度最高的城市,派遣战绩第二高的士兵潜入所选择的城市中危险程度次高的城市,以此类推(即派遣战绩第i高的士兵潜入所选择城市中危险程度第i 高的城市)。D 国有n 个城市,n - 1 条双向道路连接着这些城市,使得这些城市两两之间都可以互相到达。为了任务执行顺利,C 国选出的s 个城市中,任意两个所选的城市,都可以不经过未被选择的城市互相到达。
Access Globe 操控的士兵的战绩是第k 高,他希望能估计出最终自己潜入的城市的 危险程度。Access Globe 假设C 国是以等概率选出任意满足条件的城市集合S ,他希望你帮他求出所有可能的城市集合中,Access Globe 操控的士兵潜入城市的危险程度之和。如果选择的城市不足k 个,那么Access Globe 不会被派出,这种情况下危险程度为0。
当然,你并不想帮他解决这个问题,你也不打算告诉他这个值除以998 244 353 的 余数,你只打算告诉他这个值除以64,123 的余数。
(color{#0066ff}{输入格式})
从文件coat.in 中读入数据。
第1 行包含3 个整数n、k、W,表示D 国城市的个数、Access Globe 所操控士兵 潜入的城市战绩排名以及D 国的所有城市中最大的危险程度;
第2 行包含n 个1 到W 之间的整数(d_1); (d_2); ... (d_n),表示每个城市的危险程度;
第3 行到第n + 1 行,每行两个整数(x_i); (y_i),表示D 国存在一条连接城市(x_i) 和城市(y_i) 的双向道路。
(color{#0066ff}{输出格式})
输出到文件coat.out 中。
输出一个整数,表示所有可行的城市集合中,Access Globe 操控的士兵潜入城市的危险程度之和除以64,123 的余数。
(color{#0066ff}{输入样例})
5 3 3
2 1 1 2 3
1 2
2 3
1 4
1 5
10 2 3
2 1 1 3 1 2 3 3 1 3
1 2
2 3
2 4
2 5
2 6
5 7
1 8
8 9
1 10
(color{#0066ff}{输出样例})
11
435
(color{#0066ff}{数据范围与提示})
D 国地图如下,其中危险程度为d 的城市的形状是(d + 3) 边形。
以下是所有符合条件且选择的城市不少于3 个的方案:
• 选择城市1、2、3,Access Globe 的士兵潜入的城市危险程度为1;
• 选择城市1、2、3、4,Access Globe 的士兵潜入的城市危险程度为1;
• 选择城市1、2、3、5,Access Globe 的士兵潜入的城市危险程度为1;
• 选择城市1、2、3、4、5,Access Globe 的士兵潜入的城市危险程度为2;
• 选择城市1、2、4,Access Globe 的士兵潜入的城市危险程度为1;
• 选择城市1、2、5,Access Globe 的士兵潜入的城市危险程度为1;
• 选择城市1、2、4、5,Access Globe 的士兵潜入的城市危险程度为2;
• 选择城市1、4、5,Access Globe 的士兵潜入的城市危险程度为2;而在选择的 城市少于3 时,Access Globe 的士兵潜入的城市危险程度均为0;
所以你应该输出(1 + 1 + 1 + 2 + 1 + 1 + 2 + 2) mod 64 123 = 11。
7000ms / 1024MB
(color{#0066ff}{题解})
考虑正解暴力碾标算
这种数据范围,可以想到树形DP
答案就是所有联通块的第k大之和
对于每个联通块求第k大是不太好弄的
可以转化一下
我们枚举一个下界x,(ans+=第k大ge x的联通块个数)
这样的话,比如第k大是5,那么我们从1枚举到5,每次都会算一遍,对答案的贡献是正确的
然后就可以DP了
(f[i][j]为以i为根子树选j个ge x的点且与i联通的联通块个数)
跑树形背包即可
上界(O(n^3))
卡卡常数就能碾过!
// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define LL long long
LL in() {
char ch; LL x = 0, f = 1;
while(!isdigit(ch = getchar()))(ch == '-') && (f = -f);
for(x = ch ^ 48; isdigit(ch = getchar()); x = (x << 1) + (x << 3) + (ch ^ 48));
return x * f;
}
const int maxn = 2050;
const int mod = 64123;
struct node {
int to;
node *nxt;
node(int to = 0, node *nxt = NULL): to(to), nxt(nxt) {}
};
node *head[maxn];
int f[maxn][maxn], n, k, w, s[maxn], val[maxn], ans, siz[maxn];
void add(int from, int to) {
head[from] = new node(to, head[from]);
}
void dfs(int x, int fa, int limit) {
f[x][siz[x] = val[x] >= limit] = 1;
for(node *i = head[x]; i; i = i->nxt) {
if(i->to == fa) continue;
dfs(i->to, x, limit);
for(int l = siz[x]; l >= 0; l--) {
if(f[x][l]) {
for(int r = siz[i->to]; r >= 0; r--) {
if(f[i->to][r])
(f[x][l + r] += 1LL * f[x][l] * f[i->to][r] % mod) %= mod;
}
}
}
siz[x] += siz[i->to];
}
for(int i = k; i <= siz[x]; i++) (ans += f[x][i]) %= mod;
}
int main() {
n = in(), k = in(), w = in();
for(int i = 1; i <= n; i++) s[val[i] = in()]++;
int x, y;
for(int i = 1; i < n; i++) x = in(), y = in(), add(x, y), add(y, x);
for(int i = w; i >= 1; i--) s[i] += s[i + 1];
for(int now = 1; now <= w; now++) {
if(s[now] < k) break;
for(int i = 0; i <= n; i++)
for(int j = 0; j <= n; j++)
f[i][j] = 0;
dfs(1, 0, now);
}
printf("%d
", ans);
return 0;
}