水题—-奶牛政坛
题目如下:
问题描述
农夫约翰的奶牛住在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;
}
点分治就是一个大暴力!