解题:POI 2004 String

题面

首先我们要有一个明确的构造思路

对于非根节点,我们把子树连上来的线两两配对,这样如果它有奇数个子树就会剩一个,这时候把这根线传给父亲即可。对于根节点还是两两配对,但是注意如果它也有奇数个子树就不能剩了,必须把这根线算上。这样第一问的答案就是每个非根节点贡献度数除以二下取整,根节点贡献度数除以二上取整

第二问我们先二分答案,仍然沿用这个思路,这时我们要让最长的最短,于是我们每次把子树里传上来的线塞进一个multiset。讨论:对于有奇数个子树的情况,从大到小枚举线,二分出和当前的线拼起来不超过二分的$mid$的最长的线,然后把它们删掉,最后剩下的那根传给父亲。注意如果有一根线找不到配对也不一定失败,我们先视为把它传给父亲,然后看看剩下的还能不能拼好。对于有偶数个子树的情况,我们直接塞一个零进multiset里去,然后按奇数的方法做,这是等价的。对于根节点,我们去除掉塞零和留线这两个操作判定即可。

 1 #include<set>
 2 #include<cstdio>
 3 #include<cstring>
 4 #include<algorithm>
 5 using namespace std;
 6 const int N=10005;
 7 int p[N],noww[2*N],goal[2*N],deg[N],dp[N];
 8 int n,t1,t2,cnt,mins,l,r,mid,ans;
 9 multiset<int> se;
10 multiset<int>::iterator it;
11 void link(int f,int t)
12 {
13     noww[++cnt]=p[f];
14     goal[cnt]=t,p[f]=cnt;
15 }
16 bool check(int nde,int fth)
17 {
18     if(deg[nde]==1&&nde!=1) 
19         {dp[nde]=0; return true;}
20     for(int i=p[nde];i;i=noww[i])
21         if(goal[i]!=fth&&!check(goal[i],nde))
22             return false;
23     se.clear();
24     for(int i=p[nde];i;i=noww[i])
25         if(goal[i]!=fth)
26             se.insert(dp[goal[i]]+1); 
27     if(nde==1)
28     {
29         while(se.size()>1)
30         {
31             long long mem=*(--se.end());
32             se.erase(--se.end()); it=se.upper_bound(mid-mem); 
33             if(it!=se.begin()) it--; else return false;
34             if((*it)+mem>mid) return false; se.erase(it);
35         }
36         if(se.size()) return (*se.begin())<=mid;
37         else return true;
38     }
39     else
40     {
41         int must=0;
42         if(se.size()%2==0) se.insert(0);
43         while(se.size()>1)
44         {
45             long long mem=*(--se.end()); 
46             se.erase(--se.end()); it=se.upper_bound(mid-mem); 
47             if(it!=se.begin()) it--; else return false; 
48             if((*it)+mem>mid) 
49             {
50                 if(must) return false;
51                 else
52                 {
53                     must=*(--se.end());
54                     continue;
55                 }
56             }
57             else se.erase(it);
58         }
59         return (dp[nde]=must?must:(*se.begin()))<=mid;
60     }
61     return true;
62 }
63 int main ()
64 {
65     scanf("%d",&n);
66     for(int i=1;i<n;i++)
67     {
68         scanf("%d%d",&t1,&t2);
69         link(t1,t2),link(t2,t1);
70         deg[t1]++,deg[t2]++;
71     }
72     mins+=(deg[1]+1)/2;
73     for(int i=2;i<=n;i++)
74         mins+=(deg[i]-1)/2;
75     l=1,r=n;
76     while(l<=r)
77     {
78         mid=(l+r)/2;
79         if(check(1,0)) r=mid-1,ans=mid;
80         else l=mid+1;
81     }
82     printf("%d %d",mins,ans);
83     return 0;
84 }
View Code
原文地址:https://www.cnblogs.com/ydnhaha/p/9809409.html