水题一道----奶牛政坛

水题—-奶牛政坛

题目如下:

问题描述

农夫约翰的奶牛住在N (2 <= N <= 200,000)片不同的草地上,标号为1到N。恰好有N-1条单位长度的双向道路,用各种各样的方法连接这些草地。而且从每片草地出发都可以抵达其他所有草地。也就是说,这些草地和道路构成了一种叫做树的图。

输入包含一个详细的草地的集合,详细说明了每个草地的父节点P_i (0 <= P_i <= N)。根节点的P_i == 0, 表示它没有父节点。因为奶牛建立了1到K一共K (1 <= K <= N/2)个政党。每只奶牛都要加入某一个政党,其中,第i只奶牛属于第A_i (1 <= A_i <= K)个政党。而且每个政党至少有两只奶牛。这些政党互相吵闹争。每个政党都想知道自己的“范围”有多大。其中,定义一个政党的范围是这个政党离得最远的两只奶牛(沿着双向道路行走)的距离。比如说,记为政党1包含奶牛1,3和6,政党2包含奶牛2,4和5。

这些草地的连接方式如下图所示(政党1由-n-表示):政党1最大的两只奶牛的距离是3(也就是奶牛3和奶牛6的距离)。政党2最大的两只奶牛的距离是2(也就是奶牛2和4,4和5,还有5和2之间的距离)。帮助奶牛们求出每个政党的范围。

输入格式

第一行: 两个由空格隔开的整数: N 和 K

第2到第N+1行: 第i+1行包含两个由空格隔开的整数: A_i和P_i

输出格式

第1到第K行: 第i行包含一个单独的整数,表示第i个政党的范围。

数据规模

见问题描述

题解如下:

这就是一道大水题,我们只需要预处理出每个政党深度最深的点,然后扫一遍所有的点,用该点到对应政党最深点的距离更新该政党的范围即可。其实每个政党是散布在整个树中的,给人一种虚树的感觉。如果对于每个政党构建一颗虚树,那么深度最深的点一定是虚树的某条直径的某个端点,所以到该点的最远距离即是虚树的直径长度。其实根节点不一定在虚树中,但是把根节点加入虚树是没有影响的,这就是为什么我们可以直接处理深度最深的点.

这道题如果用点分治的话就是一个模板题。我写了一份点分治的代码,放在下面:

#include<cstdio> 
#include<algorithm> 
using namespace std; 
#define MAXN 855000 
int st[MAXN],top; 
int size[MAXN],vis[MAXN],c[MAXN],rt; 
int Next[MAXN],End[MAXN],Last[MAXN],e=1; 
void add(int x,int y) 
{ 
    End[++e]=y; 
    Next[e]=Last[x]; 
    Last[x]=e; 
    return ; 
} 
void _r(int& x) 
{ 
    char c=getchar(); 
    while(c<'0'||c>'9') 
    { 
        c=getchar(); 
    } 
    for(x=0;c>='0'&&c<='9';c=getchar()) 
    { 
        x=(x<<1)+(x<<3)+c-'0'; 
    } 
    return ; 
} 
int get(int p,int summ,int fa) 
{ 
    int sz=0,total=0,ch=0; 
    for(int t=Last[p],q;t;t=Next[t]) 
    { 
        q=End[t]; 
        if(vis[q]||q==fa) 
        { 
            continue; 
        } 
        if(size[q]>sz) 
        { 
            sz=size[q]; 
            ch=q; 
        } 
        total+=size[q]; 
    } 
    if(sz>(summ>>1)) 
    { 
        return get(ch,summ,p); 
    } 
    else 
    { 
        return p; 
    } 
} 
const int inf=0x1fffffff; 
int cur[MAXN],dis[MAXN],Ans[MAXN]; 
void solve(int p,int fa) 
{ 
    size[p]=1; 
    for(int t=Last[p];t;t=Next[t]) 
    { 
        if(!vis[End[t]]&&End[t]!=fa) 
        { 
            solve(End[t],p); 
            size[p]+=size[End[t]]; 
        } 
    } 
    dis[c[p]]=-inf; 
    return ; 
} 
void cal(int p,int fa,int dep) 
{ 
    int h=c[p]; 
    st[++top]=p; 
    Ans[h]=max(Ans[h],dis[h]+dep); 
    cur[h]=max(cur[h],dep); 
    for(int t=Last[p];t;t=Next[t]) 
    { 
        if(!vis[End[t]]&&End[t]!=fa) 
        { 
            cal(End[t],p,dep+1); 
        } 
    } 
    return ; 
} 
void dfs(int p) 
{ 
    solve(p,0); 
    p=get(p,size[p],0); 
    vis[p]=1; 
    dis[c[p]]=0; 
    for(int t=Last[p],q;t;t=Next[t]) 
    { 
        q=End[t]; 
        if(!vis[q]) 
        { 
            cal(q,p,1); 
            for(int s;top;--top) 
            { 
                s=c[st[top]]; 
                dis[s]=max(dis[s],cur[s]); 
                cur[s]=-inf; 
            } 
        } 
    } 
    for(int t=Last[p],q;t;t=Next[t]) 
    { 
        if(!vis[End[t]]) 
        { 
            dfs(End[t]); 
        } 
    } 
    return ; 
} 
int n,k; 
int main() 
{ 
    _r(n); 
    _r(k); 
    for(int i=1,x;i<=n;i++) 
    { 
        _r(c[i]); 
        _r(x); 
        if(x==0) 
        { 
            rt=i; 
        } 
        else 
        { 
            add(x,i); 
            add(i,x); 
        } 
    } 
    for(int i=1;i<=k;i++) 
    { 
        Ans[i]=0; 
    } 
    dfs(rt); 
    for(int i=1;i<=k;i++) 
    { 
        printf("%d\n",Ans[i]); 
    } 
    return 0; 
}

点分治就是一个大暴力!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值