水題—-奶牛政壇
題目如下:
問題描述
農夫約翰的奶牛住在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;
}
點分治就是一個大暴力!