bzoj4871 [Heoi2017]摧毁“树状图”

刷完了去年的省选题,发现自己dp已经凉凉了。

虽然暴力可以拿到80分的好成绩,但是正解的dp状态和转移还是没能想到,是时候补一波dp了

这道题我们肯定是要树形dp,存的肯定就是子树某种状态的最多的联通块数,那么我们发现有这么几个因素会影响转移

1.子树的根是否被删掉

2.是否有一条链可以从子树中伸出去,即根连的链数是不是奇数

3.子树中共出现了几条路径

那么f[i][0/1(n点删不删)][0/1(能否向上扩展)][0/1/2(子树中共有几条路径)]就是我们的数组定义

转移时我是直接让新的子树和已有的子树信息进行合并,也需要额外记录两个信息,即当前共有几棵子树和当前子树中只有1条路径时的删去根结点时的信息。

看起来状态定义十分麻烦,但是实际只有6个状态是有用的,手动转移,理解了就比较简单。

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <iostream>
 4 #include <algorithm>
 5 #include <cmath>
 6 #define N 100500
 7 using namespace std;
 8 int e=1,head[N];
 9 struct edge{
10     int v,next;
11 }ed[N<<1];
12 inline void add(int u,int v){
13     ed[e].v=v;
14     ed[e].next=head[u];
15     head[u]=e++;
16 }
17 int T,opt,n,ans;
18 int f[N][2][2][3];//f[i][0/1(n点删不删)][0/1(能否向上扩展)][0/1/2(子树中共有几条路径)]
19 inline void gmax(int &x,int y){x=x>y?x:y;}
20 inline int Max(int x,int y){return x>y?x:y;}
21 void dfs(int x,int fa){
22     int son=0,t=0;
23     for(int i=head[x];i;i=ed[i].next){
24         int v=ed[i].v;
25         if(v==fa)continue;
26         dfs(v,x);
27         int f1,f2,f3,f4,f5,f6;
28         f1=f[x][0][0][1];
29         f2=f[x][0][0][2];
30         f3=f[x][1][1][1]+1;
31         f4=f[x][1][0][1]+1;
32         f5=f[x][1][1][2]+1;
33         f6=f[x][1][0][2]+1;
34         gmax(f1,f[v][0][0][1]);
35         gmax(f1,f[v][1][0][1]+1);
36         gmax(f1,f[v][1][1][1]+1);
37 
38         gmax(f2,f[v][0][0][2]);
39         gmax(f2,f[v][1][0][2]+1);
40         gmax(f2,f[v][1][1][2]+1);
41         gmax(f2,f[x][0][0][1]+f[v][0][0][1]-1);
42         gmax(f2,f[x][0][0][1]+f[v][1][0][1]);
43         gmax(f2,f[x][0][0][1]+f[v][1][1][1]);
44 
45         gmax(f3,f[v][1][1][1]+son);
46         gmax(f4,f[x][1][1][1]+f[v][1][1][1]);
47 
48         gmax(f5,f[v][1][1][2]+son);
49         gmax(f5,f[x][1][1][1]+f[v][0][0][1]);
50         gmax(f5,f[x][1][1][1]+f[v][1][0][1]);
51         gmax(f5,f[x][1][1][1]+f[v][1][1][1]); 
52         gmax(f5,t+f[v][1][1][1]);
53         gmax(f5,f[x][1][0][1]+f[v][1][1][1]);
54 
55         gmax(f6,f[x][1][0][1]+f[v][0][0][1]);
56         gmax(f6,f[x][1][0][1]+f[v][1][0][1]);
57         gmax(f6,f[x][1][1][2]+f[v][1][1][1]);
58         gmax(f6,f[x][1][1][1]+f[v][1][1][2]);
59 
60         t=Max(t+1,son+Max(f[v][0][0][1],Max(f[v][1][0][1],f[v][1][1][1])));
61         son++;
62 
63         f[x][0][0][1]=f1;
64         f[x][0][0][2]=f2;
65         f[x][1][1][1]=f3;
66         f[x][1][0][1]=f4;
67         f[x][1][1][2]=f5;
68         f[x][1][0][2]=f6;
69     }
70     gmax(f[x][1][1][1],son);
71 }
72 inline void init(){
73     e=1;
74     for(int i=1;i<=n;++i)
75         memset(f[i],0,sizeof f[i]),head[i]=0;
76 }
77 inline int read(){
78     int a=0;char ch=getchar();
79     while(ch<'0'||ch>'9')ch=getchar();
80     while(ch>='0'&&ch<='9')a=a*10+(ch^48),ch=getchar();
81     return a;
82 }
83 int main(){
84     scanf("%d%d",&T,&opt);
85     while(T--){
86         n=read();
87         init();
88         for(int i=1;i<=opt;++i)read(),read();
89         for(int i=1,u,v;i<n;++i){
90             u=read();v=read();
91             add(u,v);add(v,u);
92         }
93         dfs(1,0);
94         ans=Max(f[1][0][0][2],Max(f[1][1][0][2],f[1][1][1][2]));
95         printf("%d\n",ans);
96     }
97     return 0;
98 }
View Code

 

posted @ 2018-03-24 14:26  Ren_Ivan  阅读(279)  评论(0编辑  收藏