解题:POI 2013 Triumphal arch

题面

 二分答案,问题就转化为了一个可行性问题,因为我们不知道国王会往哪里走,所以我们要在所有他可能走到的点建造,考虑用树形DP解决(这个DP还是比较好写的,你看我这个不会DP的人都能写出来=。=)

定义$dp[x]$表示以$x$这个点为根的子树中(不包含x)需要修建的次数(因为1号点已经修好了,最后回来不用管),那么对于每个二分出的$mid$有$dp[x]=max((sum dp[son[i]])+sons[x]-mid)$,其中$sons[i]$表示它(直接的)儿子的个数,显然对于每个点是必须先把所有儿子建好的。最后如果$dp[1]=0$说明可行

 1 #include<cstdio>
 2 #include<cstring>
 3 #include<algorithm>
 4 using namespace std;
 5 const int N=300005;
 6 int n,t1,t2,cnt,l,r,mid,ans;
 7 int p[N],noww[2*N],goal[2*N];
 8 int son[N],dp[N];
 9 void link(int f,int t)
10 {
11     noww[++cnt]=p[f];
12     goal[cnt]=t,p[f]=cnt;
13 }
14 void MARK(int nde,int fth)
15 {
16     for(int i=p[nde];i;i=noww[i])
17         if(goal[i]!=fth) MARK(goal[i],nde),son[nde]++;
18 }
19 void DFS(int nde,int fth)
20 {
21     int tmp=0;
22     for(int i=p[nde];i;i=noww[i])
23         if(goal[i]!=fth) DFS(goal[i],nde),tmp+=dp[goal[i]];
24     dp[nde]=max(tmp+son[nde]-mid,0);
25 }
26 bool check(int x)
27 {
28     memset(dp,0xcf,sizeof dp);
29     DFS(1,0); return !dp[1];
30 }
31 int main ()
32 {
33     scanf("%d",&n),r=n;
34     for(int i=1;i<n;i++)
35     {
36         scanf("%d%d",&t1,&t2);
37         link(t1,t2),link(t2,t1);
38     }
39     MARK(1,0);
40     while(l<=r)
41     {
42         mid=(l+r)/2;
43         if(check(mid)) r=mid-1,ans=mid;
44         else l=mid+1;
45     }
46     printf("%d",ans);
47     return 0;
48 }
View Code
原文地址:https://www.cnblogs.com/ydnhaha/p/9682674.html