【HAOI2009】【P1307】毛毛虫

感觉相比其他树归题简单多了,不过有点绕(也许是我的思路很奇怪一。一)(这是省选题啊,就算作为T1这题也太水了,HA好弱……)

原题:

对于一棵树,我们可以将某条链和与该链相连的边抽出来,看上去就象成一个毛毛虫,点数越多,毛毛虫就越大。例如下图左边的树(图1)抽出一部分就变成了右边的一个毛毛虫了(图2)。

N≤300000

先搞出无根树,这回不枚举中间点了,说说我的奇怪的做法一。一

搞两个数组,一个是uf,表示包括自己在内的直系最大值,另一个是bf,表示x和x往下的兄弟中uf最大的一个

然后就是求uf和bf

uf[x]=bf[tree[x].child]+tree[x].cnum;//不用减uf最大的内个儿子,因为还有自己

如果x是叶子,也就是child[x]==0,uf[x]=1

bf[x]=max(uf[x],bf[tree[x].brother]);

因为根不一定在答案上,所以设一个全局最大值ans,求uf和bf后,ans=max(ans,uf[x]+bf[tree[x].brother]+tree[tree[x].father].cnum-(tree[x].father==1));

这里不用减去两个儿子,因为还有爹和爷,然而当tree[x].father==1(我把根设为1)的时候要-1,因为根没有爹

最后直接输出ans即可

(用全局最大值来更新答案应该是很多DP的策略)

代码:

 1 #include<iostream>
 2 #include<cstdio>
 3 #include<algorithm>
 4 #include<cstring>
 5 #include<cmath>
 6 using namespace std;
 7 int read(){int z=0,mark=1;  char ch=getchar();
 8     while(ch<‘0‘||ch>‘9‘){if(ch==‘-‘)mark=-1;  ch=getchar();}
 9     while(ch>=‘0‘&&ch<=‘9‘){z=(z<<3)+(z<<1)+ch-‘0‘;  ch=getchar();}
10     return z*mark;
11 }
12 struct ddd{int next,y;}e[610000];int LINK[310000],ltop=0;
13 inline void insert(int x,int y){e[++ltop].next=LINK[x];LINK[x]=ltop;e[ltop].y=y;}
14 struct dcd{int brother,child,father,  cnum;}tree[310000];
15 inline void insert_tree(int x,int y){tree[y].brother=tree[x].child;tree[x].child=y;tree[y].father=x;  tree[x].cnum++;}
16 int n,m;
17 int uf[310000],bf[310000];//bf表示众多兄弟中谁最大,uf表示直系最大
18 int ans=0;
19 void get_tree(int x){
20     for(int i=LINK[x];i;i=e[i].next)if(e[i].y!=tree[x].father){
21         insert_tree(x,e[i].y);
22         get_tree(e[i].y);
23     }
24 }
25 void dp_tree(int x){
26     if(!x)  return ;
27     dp_tree(tree[x].brother);
28     if(tree[x].child){
29         dp_tree(tree[x].child);
30         uf[x]=bf[tree[x].child]+tree[x].cnum;//不用减uf最大的内个儿子,因为还有自己
31     }
32     else
33         uf[x]=1;
34     bf[x]=max(uf[x],bf[tree[x].brother]);
35     ans=max(ans,uf[x]+bf[tree[x].brother]+tree[tree[x].father].cnum-(tree[x].father==1));//不用减去两个儿子,因为还有爹和爷
36 }
37 int main(){//freopen("ddd.in","r",stdin);
38     memset(uf,0,sizeof(uf));
39     memset(bf,0,sizeof(bf));
40     cin>>n>>m;//其实m就等于n-1吧一。一
41     int _left,_right;
42     for(int i=1;i<=m;i++){  _left=read(),_right=read();  insert(_left,_right),insert(_right,_left);}
43     get_tree(1);
44     dp_tree(1);
45     cout<<ans<<endl;
46     return 0;
47 }

时间: 2024-12-25 18:51:07

【HAOI2009】【P1307】毛毛虫的相关文章

[haoi2009]毛毛虫 树形dp

这道题细节处理不少,但要AC不难: 设以i节点为根节点的子树能形成的最大的毛毛虫长度为f[i],则f[i]=max(f[j])+i节点的孩子数: 答案需要f最大和次大的两个子树合并,而且若合并的位置不是根节点,ans++: 我就是坑在了最后一点上,最后打表找到了问题: 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 using namespace std; 5 const int maxn=30

【HAOI2009】【毛毛虫】【树形dp】

试题描述 对于一棵树,我们可以将某条链和与该链相连的边抽出来,看上去就象成一个毛毛虫,点数越多,毛毛虫就越大.例如下图左边的树(图 1 )抽出一部分就变成了右边的一个毛毛虫了(图 2 ). 输入数据 在文本文件 worm.in 中第一行两个整数 N , M ,分别表示树中结点个数和树的边数. 接下来 M 行,每行两个整数 a, b 表示点 a 和点 b 有边连接( a, b ≤ N ).你可以假定没有一对相同的 (a, b) 会出现一次以上. 输出数据 在文本文件 worm.out 中写入一个整

[HAOI2009]毛毛虫

题目描述 对于一棵树,我们可以将某条链和与该链相连的边抽出来,看上去就象成一个毛毛虫,点数越多,毛毛虫就越大.例如下图左边的树(图 1 )抽出一部分就变成了右边的一个毛毛虫了(图 2 ). 输入输出格式 输入格式: 在文本文件 worm.in 中第一行两个整数 N , M ,分别表示树中结点个数和树的边数. 接下来 M 行,每行两个整数 a, b 表示点 a 和点 b 有边连接( a, b ≤ N ).你可以假定没有一对相同的 (a, b) 会出现一次以上. 输出格式: 在文本文件 worm.o

P3174 [HAOI2009]毛毛虫

题目描述 对于一棵树,我们可以将某条链和与该链相连的边抽出来,看上去就象成一个毛毛虫,点数越多,毛毛虫就越大.例如下图左边的树(图 1 )抽出一部分就变成了右边的一个毛毛虫了(图 2 ). 输入输出格式 输入格式: 在文本文件 worm.in 中第一行两个整数 N , M ,分别表示树中结点个数和树的边数. 接下来 M 行,每行两个整数 a, b 表示点 a 和点 b 有边连接( a, b ≤ N ).你可以假定没有一对相同的 (a, b) 会出现一次以上. 输出格式: 在文本文件 worm.o

[HAOI2009] 毛毛虫 - 树的直径

在一棵树中,一条链及与它直接相连的所有边的集合称作一个毛毛虫,这个子图中的点数称作这个毛毛虫的大小.求一棵树中最大的毛毛虫.\(N\leq 3\times10^5\) Solution 设每个点的权值为 \(deg_i-1\),然后求最长路即可,答案就是 \(ans+2\) #include <bits/stdc++.h> using namespace std; const int N = 300005; vector <int> g[N]; int n,m,vis[N],a[N

P3174 [HAOI2009]毛毛虫 题解

CSDN同步 原题链接 简要题意: 给定一棵树,求最长的 "挂链" 长度. 挂链定义为:一条链上所有节点与其相连的节点构成的生成树.(非严谨定义)(原题中是 "毛毛虫",本人以为挂链更形象) 这题有多种做法,这里给出思路,以及其中一种做法的代码. 算法一 注意到,其实我们只需要选出 "最长链",然后在最长链的两侧挂链即可. 即,先求出 树的直径 的两个端点,然后遍历一遍直径上的端点,把它们的直接连边都加入生成树中. 最后统计答案即可. 时间复杂度

题解——[HAOI2009]毛毛虫 树形DP

题意: 给你一棵树,从树中取出一部分满足:是一条链+一些直接连在这条链上的节点 求节点数最多的合法取出部分. 题解: 其实这题还是不难? 观察到对于任意一条链, 只有两种情况: 一条路走到底 or  以某个点为中转 f[x]表示从x往下走,一路走到底的包括x的最优解, f[x]包括x也包括father[x](将会加入它的贡献) 观察到以某个点为中转的情况: 倘若某条链以一个点为中转,那么这条链将无法向上产生贡献, 若没有,则变为第一种情况,且一定可以向上产生贡献, 以点x为中转的所有链都可以通过

【Luogu P3174 】[HAOI2009]毛毛虫

前言: 虽然很多人和我想法一样 ,但我还是不要脸地写了这题解 题目: 链接 大意: 在一棵树上取一条最长链以及它所连接的结点总共的结点个数 思路: 取链: 用树形\(DP\)就可以轻而易举的解决这个问题: \(f_x\)表示以\(x\)为根节点的树的深度 转移方程: \[f_x=max\{f_y + 1 \} (y\in son(x))\] 那么以\(x\)为根节点的树的最长链就是\(f_x\)加上次大的子树深度,下方代码区以\(ans\)来表示. 代码: void dp(int x, int

2431: [HAOI2009]逆序对数列

2431: [HAOI2009]逆序对数列 Time Limit: 5 Sec  Memory Limit: 128 MBSubmit: 954  Solved: 548[Submit][Status] Description 对于一个数列{ai},如果有i<j且ai>aj,那么我们称ai与aj为一对逆序对数.若对于任意一个由1~n自然数组成的数列,可以很容易求出有多少个逆序对数.那么逆序对数为k的这样自然数数列到底有多少个? Input 第一行为两个整数n,k. Output 写入一个整数,