[atARC088F]Christmas Tree

合并具有交换律,因此即将一个连通块(初始为空)与一条链合并(其中各选1点,初始直接替换)

把插入改为染色,等价于对树上的一条链(包括点和边)染色,其中恰好有1个已经被染色的点(初始任意)

对于”恰有1个已被染色的点“这个条件,其实是可以忽略的,证明如下:

由于染色的点必然是一个完整的连通块,即如果包含两个被染色的点,这两个点之间的路径也已经被染色,完全可以在第一次添加时延长下去

对于存在被染色的点,只需要找到一个已经被染色的点(仍有出边未被染色),先操作经过其的操作即可

由此,题目即变为通过$A$次染长度不超过$B$的链,使得所有点和边(其实仅需要考虑边)都被染色

先考虑最小的$A$,若有$s$个奇数度数的点,则$A\ge \frac{s}{2}$(显然$s$为偶数),这是因为每一条链至多改变两个点度数的奇偶性,而初始都为偶数,因此至少$\frac{s}{2}$条链

同时,每一次选择两个奇数度数的点连结,类似重心的构造方法也可以得到一组合法方案

最小的$B$同noip2018D1T3,总时间复杂度为$o(n\log^{2}n)$

 1 #include<bits/stdc++.h>
 2 using namespace std;
 3 #define N 100005
 4 struct ji{
 5     int nex,to;
 6 }edge[N<<1];
 7 vector<int>v;
 8 int E,n,x,y,sum,ans,head[N],r[N],g[N],f[N];
 9 void add(int x,int y){
10     edge[E].nex=head[x];
11     edge[E].to=y;
12     head[x]=E++;
13 }
14 int calc(int k){
15     int s=0;
16     for(int i=v.size()-1,j=0;i>=j;i--){
17         if (i==k)i--;
18         if (j==k)j++;
19         if (i>=j){
20             s++;
21             if (v[i]+v[j]<=ans)j++;
22         }
23     }
24     return s;
25 }
26 void dfs(int k,int fa){
27     for(int i=head[k];i!=-1;i=edge[i].nex)
28         if (edge[i].to!=fa){
29             dfs(edge[i].to,k);
30             f[k]+=f[edge[i].to];
31         }
32     v.clear();
33     for(int i=head[k];i!=-1;i=edge[i].nex)
34         if (edge[i].to!=fa){
35             if (g[edge[i].to]+1==ans)f[k]++;
36             else v.push_back(g[edge[i].to]+1);
37         }
38     if (!v.size())return;
39     sort(v.begin(),v.end());
40     int l=-1,r=v.size()-1,s=calc(r);
41     while (l<r){
42         int mid=(l+r>>1);
43         if (calc(mid)==s)r=mid;
44         else l=mid+1;
45     }
46     f[k]+=s;
47     if (l<0)g[k]=0;
48     else g[k]=v[l];
49 }
50 bool pd(int k){
51     ans=k;
52     memset(f,0,sizeof(f));
53     memset(g,0,sizeof(g));
54     dfs(1,0);
55     return (f[1]+(g[1]>0)<=sum/2);
56 }
57 int main(){
58     scanf("%d",&n);
59     memset(head,-1,sizeof(head));
60     for(int i=1;i<n;i++){
61         scanf("%d%d",&x,&y);
62         add(x,y);
63         add(y,x);
64         r[x]^=1;
65         r[y]^=1; 
66     }
67     for(int i=1;i<=n;i++)
68         if (r[i])sum++;
69     printf("%d ",sum/2);
70     int l=1,r=n;
71     while (l<r){
72         int mid=(l+r>>1);
73         if (pd(mid))r=mid;
74         else l=mid+1;
75     }
76     printf("%d",l);
77 }
View Code

 

posted @ 2020-12-14 08:57  PYWBKTDA  阅读(143)  评论(0编辑  收藏  举报