luogu P3174 [HAOI2009]毛毛虫
题面传送门
实在写不动AC自动机于是跑来写dp了。
考虑这个怎么转化。
首先可以转化为求最大边数然后加一。
左右端点一定有一种最优情况不是叶子节点,因为如果是叶子节点那么叶子节点上面那个也行。
之后计算一下每个点的度,每个点的贡献是度-1,即减去与其父节点的连边的重复贡献。
然而你会发现根节点多减了一,边数还要加一,所以最后答案还要加二。
然后就变成了点权的树的直径,这个东西dp或者其它随便乱搞一下就好了。
代码实现:
#include<cstdio>
#define beg(x) int cur=s.h[x]
#define end cur
#define go cur=tmp.z
#define max(a,b) ((a)>(b)?(a):(b))
using namespace std;
int n,m,k,x,y,z,siz[300039],dp[300039],ans=-1e9,g[300039];
struct yyy{int to,z;};
struct ljb{
int head,h[300039];yyy f[600039];
inline void add(int x,int y){f[++head]=(yyy){y,h[x]};h[x]=head;}
}s;
inline void dfs(int x,int last){
yyy tmp;g[x]=dp[x]=siz[x];
for(beg(x);end;go){
tmp=s.f[cur];
if(tmp.to^last) dfs(tmp.to,x),dp[x]=max(g[x]+g[tmp.to],dp[x]),g[x]=max(g[tmp.to]+siz[x],g[x]);
}
}
int main(){
freopen("1.in","r",stdin);
register int i;
scanf("%d%d",&n,&m);
while(m--) scanf("%d%d",&x,&y),siz[x]++,siz[y]++,s.add(x,y),s.add(y,x);
for(i=1;i<=n;i++) siz[i]--;
dfs(1,0);
for(i=1;i<=n;i++) ans=max(ans,dp[i]);
printf("%d\n",ans+2);
}