【牛客OI周赛15-提高组】题解[补题A]

牛客OI周赛15-提高组
https://ac.nowcoder.com/acm/contest/4912

A 环球旅行

给出一棵带边权的树。删除一条边,使分成的两棵树中较大的直径尽量小。求该直径。
我自己的思路:计算直径上的每条边删除后左子树的直径l和右子树的直径r;答案就是min(所有边的max(l,r))
超时,只能得50分

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
using namespace std;
const int N=1e6+5;
int n,tot,rt,dis[N],fa[N],h[N],ne[N<<1],to[N<<1],edgew[N<<1];
void addEdge(int u,int v,int w){to[++tot]=v;ne[tot]=h[u];h[u]=tot;edgew[tot]=w;}
void dfs_getD(int u,int father)
{
  fa[u]=father;
  if(dis[u]>dis[rt]) rt=u;
  for(int i=h[u];i;i=ne[i])if(to[i]!=father)
  {
      int v=to[i];
      dis[v]=dis[u]+edgew[i];
      dfs_getD(v,u);
  }
}
int si=0,b[N];
int max_dia;
void dfs(int u,int father)//求直径
{
  dis[u]=0;
  for(int i=h[u];i;i=ne[i])if(to[i]!=father)
  {
      int v=to[i];
      dfs(v,u);
      max_dia=max(max_dia,dis[u]+dis[v]+edgew[i]);
      dis[u]=max(dis[u],dis[v]+edgew[i]);
  }
}
int getTedgewoSubtree_maxDia(int x)
{
  max_dia=0;
  dfs(b[x],b[x-1]);
  int tmpMax=max_dia;
  max_dia=0;
  dfs(b[x-1],b[x]);
  return max(tmpMax,max_dia);
}
int main()
{
    scanf("%d", &n);
    for (int i = 1; i < n; i++)
    {
        int u, v, w;
        scanf("%d%d%d", &u, &v, &w);
        addEdge(u, v, w);
        addEdge(v, u, w);
    }
    dis[1] = 0;
    dfs_getD(1, 0);
    dis[rt] = 0;
    dfs_getD(rt, 0);
    //直径路径保存到b[]
    b[++si] = rt;
    while (fa[b[si]] != 0)
        b[si + 1] = fa[b[si]], si++;
    int ans = INF;
    for (int i = si; i > 1; i--)
        ans = min(ans, getTedgewoSubtree_maxDia(i));
    cout << ans << endl;
}

参考大佬的思路:

https://blog.nowcoder.net/n/53c3058e99364b9fb57bcdd830e4295c

我通俗(小白)的来理解理解大佬的思路……
最重要的一点:删除的一定是直径上的边!
假设不断直径上的边,那么树的直径不变,答案就仍然是那条直径长度
找出树的任意一条直径,让直径的序列为,直径的长度为d,取直径的一半(向上取整,7的话取4)为len,只要在直径序列中找到一个点i满足(dis为一个点到根的距离)b1到bi的距离<=d且距离也小于等于d,断开的边,求两个子树直径的最大值就是答案。
如果答案没经过直径上的点呢???

  • 假设断掉之后,答案路径没有经过原树直径上的点(答案就是子树直径的最大值),那断开其他边(上面分析了只能断原树直径上的边)只会大于等于答案

  • 如果答案路径经过直径上的点,那么一定是原树直径上的某个点k是LCA(某个子树的某个叶子节点v,或者),即v->k->b1/bn这条路径。那么我们就要最小化k->b1/bn的距离,取直径的一半(向上取整)就可以了。。。
    感叹一下:卧槽 大佬真强。。。代码加了注释,希望大家都能看懂大佬的思路

    #include<bits/stdc++.h>
    #define INF 0x3f3f3f3f
    using namespace std;
    const int N=1e6+5;
    int n,tot,rt,dis[N],fa[N],h[N],ne[N<<1],to[N<<1],edgew[N<<1];
    void addEdge(int u,int v,int w){to[++tot]=v;ne[tot]=h[u];h[u]=tot;edgew[tot]=w;}
    void dfs_getD(int u,int father)
    {
      fa[u]=father;
      if(dis[u]>dis[rt]) rt=u;
      for(int i=h[u];i;i=ne[i])if(to[i]!=father)
      {
          int v=to[i];
          dis[v]=dis[u]+edgew[i];
          dfs_getD(v,u);
      }
    }
    int si=0,b[N];
    int max_dia;
    void dfs(int u,int father)//求直径
    {
      dis[u]=0;
      for(int i=h[u];i;i=ne[i])if(to[i]!=father)
      {
          int v=to[i];
          dfs(v,u);
          max_dia=max(max_dia,dis[u]+dis[v]+edgew[i]);
          dis[u]=max(dis[u],dis[v]+edgew[i]);
      }
    }
    int getTedgewoSubtree_maxDia(int x)
    {
      max_dia=0;
      dfs(b[x],b[x-1]);
      int tmpMax=max_dia;
      max_dia=0;
      dfs(b[x-1],b[x]);
      return max(tmpMax,max_dia);
    }
    int main()
    {
      scanf("%d",&n);
      for(int i=1;i<n;i++)
      {
          int u,v,w;scanf("%d%d%d",&u,&v,&w);
          addEdge(u,v,w);addEdge(v,u,w);
      }
      dis[1]=0;
      dfs_getD(1,0);
      dis[rt]=0;
      dfs_getD(rt,0);
      int len=dis[rt]/2+dis[rt]%2;
      //直径路径保存到b[]
      b[++si]=rt;
      while(fa[b[si]]!=0) b[si+1]=fa[b[si]],si++;
    
      for(int i=si;i>1;i--)
          if(dis[b[i]]<=len&&dis[rt]-dis[b[i-1]]<=len)
          {
              printf("%d\n",getTedgewoSubtree_maxDia(i));
              break;
          }
    }

B

耗在了A接近两个小时,结果看了一眼B。。10分钟就做出来了。。还以为A最简单。题目顺序能不坑我吗。。。
结论证明官方题解写的很清楚,就是幂为0分配n个,接下来依次给幂1,2,……分配n-1个,详见代码
证明摘自官方题解https://ac.nowcoder.com/discuss/400742
图片说明

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define ll long long
#define ENTER cout<<endl
/***********************************************************/
const int MOD = 1e9+ 7;
const int maxn=1e6+10;
const int MAXN = 1e5 + 10;
/***********************************************************/
int ans[maxn]={0};
signed main(){
    int n,x;
    ios::sync_with_stdio(false);
    cin>>n>>x;
    int num=n/x;
    int zero=0;
    int g_ans=1;
    ans[g_ans++]=0;
    while(g_ans<n){
        for(int j=1;j<=x-1 && g_ans<n;++j){
            ans[g_ans++]=zero;
        }
        zero++;
    }
    ans[0]=zero;
    for(int i=0;i<n;++i){
        cout<<ans[i]<<" ";
    }
}
全部评论

相关推荐

不愿透露姓名的神秘牛友
11-21 17:16
科大讯飞 算法工程师 28.0k*14.0, 百分之三十是绩效,惯例只发0.9
点赞 评论 收藏
分享
点赞 收藏 评论
分享
牛客网
牛客企业服务