[IOI2013]Dreaming

link

一道非常类似的题目(link)

试题大意

 给你一棵含有$n$个节点的有边权森林,问每次连边将会用$L$的代价,问你若此图通过加边成为树时的最小直径。$n \leq 5\times 10^5$

试题分析

我们可以发现若两棵树要是在合并连接的点一定与树的中心有关。树的中心指对于当$i$为根时,子树上权值和最大的最小。

为什么,应为树的直径的情况只有单独一棵树,两棵树和在一起的,且那时要是合并就是的是树的中心。但其实$CF$那题应该求树的中心也行,因为那是边权都会为$1$.

所以会发现其实应该如果说要把树建完以后会发现是一个菊花树,且根为权重最大的联通块的根。因为若是小的当根的话那么就会多算了一颗树,所以最多只需要算两颗即可。

所以说我们每次处理好中心到叶子节点的最大距离是多少,然后就可以直接去计算答案了。

并且为什么最多只要算到联通块个数$\leq 3$呢,因为刚才说了这是一颗菊花图,所以我们最多有用的其实是两颗子树。

IOI2013
#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<climits>
using namespace std;
inline int read(){
    int f=1,ans=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return f*ans;
}
const int N=500001;
struct node{
    int u,v,w,nex;
}x[N<<1];
int cnt,head[N],n,m,l;
void add(int u,int v,int w){
    x[cnt].u=u,x[cnt].v=v,x[cnt].w=w,x[cnt].nex=head[u],head[u]=cnt++;
}
int dp[N][3],dis[N],col,vis[N],son[N];
/*0最长 1 次长*/
void dfs1(int f,int fath){
    vis[f]=1;
    for(int i=head[f];i!=-1;i=x[i].nex){
        if(x[i].v==fath) continue;
        dfs1(x[i].v,f);
        if(dp[x[i].v][0]+x[i].w>=dp[f][0]){dp[f][1]=dp[f][0];dp[f][0]=dp[x[i].v][0]+x[i].w,son[f]=x[i].v;}
        else if(dp[x[i].v][0]+x[i].w>dp[f][1]){dp[f][1]=dp[x[i].v][0]+x[i].w;}
    }
    return;
}
int ans,k;
void dfs2(int f,int fath,int Dis){
    if(Dis>dp[f][0]){
        dp[f][1]=dp[f][0];dp[f][0]=Dis;son[f]=fath;
    }
    else if(Dis>dp[f][1]){dp[f][1]=Dis;}
    ans=max(ans,dp[f][0]);k=min(k,dp[f][0]);
    for(int i=head[f];i!=-1;i=x[i].nex){
        if(x[i].v==fath) continue;
        if(son[f]==x[i].v) dfs2(x[i].v,f,dp[f][1]+x[i].w);
        else dfs2(x[i].v,f,dp[f][0]+x[i].w);
    }
}
int calc[N];
signed main(){
    memset(head,-1,sizeof(head));
    n=read(),m=read(),l=read();
    for(int i=1;i<=m;i++){
        int u=read()+1,v=read()+1,w=read();
        add(u,v,w),add(v,u,w); 
    }
    for(int i=1;i<=n;i++){
        if(!vis[i]){
            dfs1(i,0);
            k=INT_MAX;
            dfs2(i,0,0);
            calc[++col]=k;
        }
    }
    sort(calc+1,calc+col+1);
    if(col>=2) ans=max(ans,calc[col]+calc[col-1]+l);
    if(col>=3) ans=max(ans,calc[col-1]+calc[col-2]+2*l);
    printf("%d",ans);
}

 

#include<iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<climits>
using namespace std;
inline int read(){
    int f=1,ans=0;char c=getchar();
    while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+c-'0';c=getchar();}
    return f*ans;
}
const int N=500001;
struct node{
    int u,v,w,nex;
}x[N<<1];
int cnt,head[N],n,m,l;
void add(int u,int v,int w){
    x[cnt].u=u,x[cnt].v=v,x[cnt].w=w,x[cnt].nex=head[u],head[u]=cnt++;
}
int dp[N][3],dis[N],col,vis[N],son[N];
/*0最长 1 次长*/
void dfs1(int f,int fath){
    vis[f]=1;
    for(int i=head[f];i!=-1;i=x[i].nex){
        if(x[i].v==fath) continue;
        dfs1(x[i].v,f);
        if(dp[x[i].v][0]+x[i].w>=dp[f][0]){dp[f][1]=dp[f][0];dp[f][0]=dp[x[i].v][0]+x[i].w,son[f]=x[i].v;}
        else if(dp[x[i].v][0]+x[i].w>dp[f][1]){dp[f][1]=dp[x[i].v][0]+x[i].w;}
    }
    return;
}
int ans,k,pos;
struct Node{
    int pos,calc;
}S[N<<1];
void dfs2(int f,int fath,int Dis){
    if(Dis>dp[f][0]){
        dp[f][1]=dp[f][0];dp[f][0]=Dis;son[f]=fath;
    }
    else if(Dis>dp[f][1]){dp[f][1]=Dis;}
    ans=max(ans,dp[f][0]);k=min(k,dp[f][0]);
    if(dp[f][0]==k) pos=f;
    for(int i=head[f];i!=-1;i=x[i].nex){
        if(x[i].v==fath) continue;
        if(son[f]==x[i].v) dfs2(x[i].v,f,dp[f][1]+x[i].w);
        else dfs2(x[i].v,f,dp[f][0]+x[i].w);
    }
}
bool cmp(Node x1,Node x2){return x1.calc<x2.calc;}
signed main(){
    memset(head,-1,sizeof(head));
    n=read(),m=read(),l=1;
    for(int i=1;i<=m;i++){
        int u=read(),v=read(),w=1;
        add(u,v,w),add(v,u,w); 
    }
    for(int i=1;i<=n;i++){
        if(!vis[i]){
            dfs1(i,0);
            k=INT_MAX;
            dfs2(i,0,0);
            S[++col].calc=k;
            S[col].pos=pos;
        }
    }
    sort(S+1,S+col+1,cmp);
    if(col>=2) ans=max(ans,S[col].calc+S[col-1].calc+l);
    if(col>=3) ans=max(ans,S[col-1].calc+S[col-2].calc+2*l);
    printf("%d\n",ans);
    for(int i=col-1;i>=1;i--) cout<<S[col].pos<<" "<<S[i].pos<<endl;
}
CF

 

转载于:https://www.cnblogs.com/si-rui-yang/p/10143596.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值