[NOIP2017模拟]树

2017.8.27 T3 1948

样例数据
输入

6
1 2
2 3
3 4
3 5
3 6

输出

8

分析:
50%:我考场上想的(其实已经接近正解了),对于每一个点,它减小的长度就是它的子树以及深度大于它的一半的祖先和这些祖先的子树(不含通往该节点的子树),然后就一个一个点搜,又枚举了深度大于它一半的祖先,如果这是个菊花图(深度不是很大)就可以全过……
例如:
这里写图片描述
如果在四号节点建边,四号节点的子树肯定全部距离变短,而三号节点距离也可变短,它的子树也都可以变短。

100%:就是把我的优化,从浅到深枚举,用上面的递推下面的。

代码
50%

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
#include<queue>
#include<set>
using namespace std;

int getint()
{
    int sum=0,f=1;
    char ch;
    for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
    if(ch=='-')
    {
        f=-1;
        ch=getchar();
    }
    for(;ch>='0'&&ch<='9';ch=getchar())
        sum=(sum<<3)+(sum<<1)+ch-48;
    return sum*f;
}

const int maxn=100010;
long long dis,res,ans=36028797018963968;
int tot,n,u,v;
int first[maxn],dep[maxn],fa[maxn],son[maxn],nxt[maxn*2],to[maxn*2];

void buildtree(int u,int v)
{
    tot++;
    nxt[tot]=first[u];
    first[u]=tot;
    to[tot]=v;
    tot++;
    nxt[tot]=first[v];
    first[v]=tot;
    to[tot]=u;
}

int dfs(int num)
{
    for(int p=first[num];p;p=nxt[p])
    {
        int v=to[p];
        if(!fa[v]&&v!=1)
        {
            fa[v]=num;
            dep[v]=dep[num]+1;
            son[num]+=dfs(v);
        }
    }

    return son[num];
}

int main()
{
    freopen("tree.in","r",stdin);
    freopen("tree.out","w",stdout);

    n=getint();
    for(int i=1;i<=n;++i)
        son[i]=1;

    for(int i=1;i<n;++i)
    {
        u=getint();
        v=getint();
        buildtree(u,v);
    }

    dfs(1);

    for(int i=2;i<=n;++i)
        dis+=dep[i];

    for(int i=2;i<=n;++i)
    {
        res=dis-son[i]*dep[i]+son[i];
        int v1=i,depfa=dep[i]/2+1;
        while(dep[fa[v1]]>=depfa)
        {
            int v2=fa[v1];
            res=res-dep[v2]*(son[v2]-son[v1])+(dep[i]-dep[v2]+1)*(son[v2]-son[v1]);
            v1=v2;
        }

        ans=min(ans,res);
    }

    cout<<ans<<endl;
    return 0;
}

100%

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string>
#include<ctime>
#include<cmath>
#include<algorithm>
#include<cctype>
#include<iomanip>
#include<queue>
#include<set>
using namespace std;

int getint()
{
    int sum=0,f=1;
    char ch;
    for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar());
    if(ch=='-')
    {
        f=-1;
        ch=getchar();
    }
    for(;ch>='0'&&ch<='9';ch=getchar())
        sum=(sum<<3)+(sum<<1)+ch-48;
    return sum*f;
}

const int N=1e5+5;
int n;
int totdis,ans;
int tot,first[N],nxt[N<<1],to[N<<1];
int dep[N],fa[N],size[N],son[N];

void addedge(int x,int y)
{
    tot++;
    nxt[tot]=first[x];
    first[x]=tot;
    to[tot]=y;
    tot++;
    nxt[tot]=first[y];
    first[y]=tot;
    to[tot]=x;
}

void dfs(int u)
{
    size[u]=1;
    for(int p=first[u];p;p=nxt[p])
    {
        int v=to[p];
        if(v==fa[u]) continue;
        fa[v]=u;
        dep[v]=dep[u]+1;
        totdis+=dep[v];
        dfs(v);
        size[u]+=size[v];
    }
}

void solve(int u,int f,int cnt,int delta)//u是当前节点,f是深度为u的父亲节点深度一半的祖先节点,cnt是由于这次路径下移有多少点距离首都的距离+1了,delta是路径在父亲节点时总共减少了多长的距离
{
    if(dep[u]>=2)显然dep=0和dep=1的时候不需要管(首都和首都连边、与首都距离为1的点和首都连边,显然这样的路没价值,不用更新答案)
    {
        delta+=size[u]-cnt;//现在减少的距离首先是当前节点及它的子树距离都减少了1,但是又有cnt个点距离增加1,所以+size[u]-cnt
        ans=min(ans,totdis-delta);//更新答案
    }

    if((dep[u]+1)/2>dep[f])
    {
        f=son[f];//更新f为u的深度一半的祖先
        if(dep[u]>2)
            cnt-=size[f]-size[son[f]];//f及其除了连接u的子树的其他子树直接向上走到首都的距离已经小于等于走到u再走新路的距离,所以路径继续下移的话它们到首都的距离不会再+1了(它们就直接往上走了),所以从路径下移导致距离+1的点中移除
    }

    for(int p=first[u];p;p=nxt[p])//路径向深处移动
    {
        int v=to[p];
        if(v==fa[u]) continue;
        int tmp1=cnt,tmp2=delta;
        if(dep[u]>=2)
            cnt+=size[u]-size[v];//u及除了到v的子树以外的子树在路径下移后到首都的距离会+1,加入到cnt中

        son[u]=v;//为了更后面的dfs处理son[f]的问题
        solve(v,f,cnt,delta);
        if(dep[u]>=2)
            cnt=tmp1,delta=tmp2;//还原走下一个点
    }
}

int main()
{
    //freopen("tree.in","r",stdin);
    //freopen("tree.out","w",stdout);

    int x,y;
    n=getint();
    for(int i=1;i<n;++i)
    {
        x=getint();y=getint();
        addedge(x,y);
    }

    dfs(1);

    ans=totdis;
    solve(1,1,0,0);//从浅到深递推
    cout<<ans<<'\n';
    return 0;
} 

本题结。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值