bzoj 3246: [Ioi2013]Dreaming 树形dp+树的直径

    xiaoxiao2021-03-25  93

    题意

    你有一个n个点m条边的森林,编号从0开始,边有边权,你现在要添加若干边权为L的边,满足: 1、最后n个点构成一颗树。 2、这棵树的直径尽量小。 请你求出这个最小的直径是多少。 n<=500000

    分析

    设d[i]表示i到其所在子树的最远的点的距离。显然每一棵树与其他树相连的必然都是同一个点,且必然是d[i]最小的那个点。然后把每棵树缩成一个点,点权为其最小的d[i]。那么最后一定是构成一个菊花图,且中间那个点必然是点权最大的那个点。 那么我们只要预处理处d[i],建好图后用两次dfs来求直径即可。

    比赛的时候想到了第一个结论,但没想到第二个结论,所以比赛的时候我的做法是动态维护d[i]最小的点:显然d[i]最小的点必然为树的直径的中点,那么只要启发式合并的同时维护直径,再用倍增大法找到其中点即可。 由于我的方法要求lca,不仅多一个log而且还被卡了一波空间2333

    代码

    #include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> #define N 500005 using namespace std; int n,m,cnt,last[N],mx1[N],mx2[N],num1[N],pts[N],ans,num,L; struct edge{int to,next,len;}e[N*2]; bool vis[N]; void addedge(int u,int v,int len) { e[++cnt].to=v;e[cnt].len=len;e[cnt].next=last[u];last[u]=cnt; e[++cnt].to=u;e[cnt].len=len;e[cnt].next=last[v];last[v]=cnt; } void dp1(int x) { vis[x]=1; for (int i=last[x];i;i=e[i].next) { if (vis[e[i].to]) continue; dp1(e[i].to); int w=mx1[e[i].to]+e[i].len; if (w>mx1[x]) mx2[x]=mx1[x],mx1[x]=w,num1[x]=e[i].to; else if (w>mx2[x]) mx2[x]=w; } } void dp2(int x,int root) { vis[x]=1; if (mx1[x]<mx1[pts[root]]||!pts[root]) pts[root]=x; for (int i=last[x];i;i=e[i].next) { if (vis[e[i].to]) continue; int w=0; if (e[i].to==num1[x]) w=mx2[x]+e[i].len; else w=mx1[x]+e[i].len; if (w>mx1[e[i].to]) mx2[e[i].to]=mx1[e[i].to],mx1[e[i].to]=w,num1[e[i].to]=x; else if (w>mx2[e[i].to]) mx2[e[i].to]=w; dp2(e[i].to,root); } } void dfs(int x,int fa,int len) { if (len>ans) ans=len,num=x; for (int i=last[x];i;i=e[i].next) if (e[i].to!=fa) dfs(e[i].to,x,len+e[i].len); } int main() { scanf("%d%d%d",&n,&m,&L); for (int i=1;i<=m;i++) { int x,y,len; scanf("%d%d%d",&x,&y,&len); addedge(++x,++y,len); } for (int i=1;i<=n;i++) if (!vis[i]) dp1(i); memset(vis,0,sizeof(vis)); for (int i=1;i<=n;i++) if (!vis[i]) dp2(i,i); int x=0; for (int i=1;i<=n;i++) if (pts[i]&&(mx1[pts[i]]>mx1[x]||!x)) x=pts[i]; for (int i=1;i<=n;i++) if (pts[i]&&pts[i]!=x) addedge(x,pts[i],L); dfs(1,0,0); x=num;ans=num=0; dfs(x,0,0); printf("%d",ans); return 0; }
    转载请注明原文地址: https://ju.6miu.com/read-25765.html

    最新回复(0)