题解UVa12186 工人的请愿书

刘汝佳大法好!

dp(u)dp(u)表示让uu号员工给上级发信至少需要多少个工人。

那么假设uu的子节点个数为kk个,则至少需要c=(kT1)/100+1c=(kT-1)/100+1个直接下属发信才可以让uu号员工给上级发信(注意以上公式括号内的1-1是为了防止kTkT100100的倍数而减一的)。

则我们更新dp(u)dp(u)的方法是:将uukk个下属中dpdp值最小的cc个做和,即可保证dp(u)dp(u)是最小的。

最后答案就是dp(0)dp(0)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
#include <bits/stdc++.h>
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define per(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
const int N=1e5+5;
int n,t;
int target[N],pre[N],last[N],tot=0;
void add(int u,int v) {
target[++tot] = v;
pre[tot] = last[u];
last[u] = tot;
}
int dfs(int u) {
if(!last[u]) return 1;
int tmp[N],cnt=0;
for(int ptr=last[u];ptr;ptr=pre[ptr]) {
tmp[++cnt]=dfs(target[ptr]);
}
sort(tmp+1,tmp+cnt+1);
int ned = (cnt*t-1)/100+1,dp=0;
rep(i,1,ned) dp+=tmp[i]; //取最小的c个dp值
return dp;
}
int main(){
while(~scanf("%d%d",&n,&t)&&n&&t) {
tot=0;
memset(last,0,sizeof(last));
rep(i,1,n) {
int x;
scanf("%d",&x);
add(x+1,i+1); //这里我把点的编号加一了,从0~n变成了1~n+1
}
printf("%d\n",dfs(1));
}
return 0;
}