2016.10.27 Tayz T3 范围(倍增LCA+伪.树的直径)

【问题描述】
蒟蒻 XT 在机房用过 n 个电脑,所有的电脑从 1 到 n 编号,并且这 n 个电脑之间的网络连接
形成树形结构(边权为 1)。蒟蒻 XT 请神犇 X 把 n 个电脑归入了 k 个局域网(k≤
2
n
),局域网
从 1 到 k 编号。保证一个电脑不会在 2 个局域网中出现。而神犇 X 要问蒟蒻 XT 的则是在同一局
域网内最远的两台电脑之间的距离。蒟蒻 XT 不会算,找到你帮忙。
【输入格式】
第一行为 n,k;
接下来 n 行,内行两个正整数,pi和 fai,分别表示第 i 号电脑归属于哪个局域网和第 i 号电脑
的父亲节点。
若 fai为 0,则表示 i 号电脑是根,数据保证每个局域网内至少两个有电脑。
【输出格式】
k 行每行一个整数,表示第 i 个局域网内最远的两台电脑之间的距离。
【输入样例】
6 2
1 3
2 1
1 0
2 1
2 1
1 5
【输出样例】
3
2
【数据说明】
对于 30% 的数据 n ≤ 100;
对于 50% 的数据 n ≤ 50000;
对于 100%的数据 n ≤ 200000。

今天在搞LCA想找水题练练手,跑去问小兔子,小兔子曰:”tayz T3。”,看了数据范围感觉不可做,小兔子曰:”LCA不打倍增和咸鱼有什么区别?”

10.26号刚搞了树的直径,27号胡策就考了这个题….
考场上直接上暴力两边BFS搜的树的直径,结果贼慢….

进入正题
树的直径,树的直径是指树的最长简单路。
怎么求?
在一个树上,从任意一个点出发,跑最长路跑到最远的点f,从f再跑最长路,跑到最远的点t,f到t之间的路径就是树的直径。

因为这个树上的边权都是1…所以路径就和深度有关啊。
从根开始往下DFS处理出每个点的深度,u节点到v节点的距离就是deep[u]+deep[v]-2*deep[LCA(u,v)];
正确性自己证一下。

对于k个局域网,我们可以用一个vector存。
vector[i][j]代表第i个局域网的第j个节点。
我们一开始随便找一个局域网i中的节点,求它和局域网内所有节点的距离,跑出比当前maxx长的距离,就把最远点更新一下,然后再求最远点到局域网i内所有节点的最远距离

#include<algorithm>
#include<cstdio>
#include<vector>
#include<cmath>
#include<cstdlib>
using namespace std;
const int maxn=200000+50;
int p[maxn][30];
struct Edge
{
    int to;
    int next;
}edge[maxn<<2];
int n,k;
int tot;
int head[maxn],deep[maxn],fa[maxn];
void add(int f,int t)
{
    edge[++tot]=(Edge){t,head[f]};
    head[f]=tot;
}
void dfs1(int f,int t)
{
    deep[t]=deep[f]+1;
    for(int i=head[t];i;i=edge[i].next)
    {
        Edge e=edge[i];
        if(!deep[e.to])
        {
            p[i][0]=t;
            dfs1(t,e.to);
        }
    }
}
void clr()
{
    for(int j=1;j<=25;j++)
    {
        for(int i=1;i<=n;i++)
        {
            if(p[i][j-1])
                p[i][j]=p[p[i][j-1]][j-1];
        } 
    }

}

int lca(int a,int b)
{
    int x=a,y=b;
    if(deep[a]<deep[b])
        swap(a,b);
    for(int j=25;j>=0;j--)
    {
        if(deep[p[a][j]]>=deep[b])
        a=p[a][j];
    }
    if(a==b)
    return abs(deep[x]-deep[y]);//一条链。 
    for(int j=25;j>=0;j--)
    {
        if(p[a][j]!=p[b][j])
            a=p[a][j],b=p[b][j];
    }
    return deep[x]+deep[y]-2*deep[p[a][0]];
}
int lan[maxn];
vector<int> v[maxn];
int main()
{
    int __size__ = 1024 << 15;
    char * __p__ = (char *) malloc(__size__) + __size__;
    __asm__("movl %0, %%esp\n" :: "r"(__p__));
    freopen("cowpol.in","r",stdin);
    freopen("cowpol.out","w",stdout);
    int root;
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)
    {
        int b,a;
        scanf("%d%d",&b,&a);
        v[b].push_back(i);
        if(a==0)
            root=i;
        add(a,i);
    }
    dfs1(200050,root);
    clr();
    for(int i=1;i<=k;i++)
    {
        int poi=0,maxx=0;
        for(int j=0;j<v[i].size();j++)
        {
            int hah=lca(v[i][0],v[i][j]);
            if(hah>maxx)
            {
                maxx=hah;
                poi=v[i][j];
            } 
        }
        maxx=0;
        for(int j=0;j<v[i].size();j++)
        {
            int hah=lca(poi,v[i][j]);
            if(hah>maxx)
                maxx=hah;
        }
        printf("%d\n",maxx);    
    }
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值