BZOJ 1131 Sta

来源:互联网 发布:星空卫视直播软件下载 编辑:程序博客网 时间:2024/06/03 18:40

1131: [POI2008]Sta

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 1558  Solved: 573
[Submit][Status][Discuss]

Description

给出一个N个点的树,找出一个点来,以这个点为根的树时,所有点的深度之和最大

Input

给出一个数字N,代表有N个点.N<=1000000 下面N-1条边.

Output

输出你所找到的点,如果具有多个解,请输出编号最小的那个.

Sample Input

8
1 4
5 6
4 5
6 7
6 8
2 4
3 4

Sample Output

7

俗话说的好,最好的题目描述往往只有一行。

貌似我的做法不是最简单的(做到最后发现有数组就没用上),不喜勿喷。

默认以1为最高祖先,开三个数组,f[i]记录以i为根的子树的节点个数(包括自己),deep[i]记录i的深度,sum[i]记录深度和。

先跑一遍大法师(DFS),处理出三个数组,然后以1为根扩展遍历所有点,假设该点为根,算出答案并比较,注意题目中的编号大小为第二关键字。

因为是逐层将根向下移,每次都是从第二层中选一个点作为新的最高祖先,所以使用一个pair,first记录节点编号,second记录以它为根的sum值(注意和sum数组并不同,这里记录的是以它为最高祖先)。

推式子:当第i个点位于第二层时(注意这个第二层是相对的,相对于目前你假设的根节点),画图不难看出所有非它子树的节点深度都会加1,所有它子树节点深度都会减1.

#include<cstdio>#include<cstring>#include<string>#include<algorithm>#include<iostream>#include<cmath>#include<queue>using namespace std;const int MAXN=1000010;struct edge{int next,to;};edge e[2*MAXN];int head[MAXN],cnt;void addedge(int u,int v){e[++cnt].next=head[u];e[cnt].to=v;head[u]=cnt;}bool visit[MAXN];long long f[MAXN],deep[MAXN],sum[MAXN];void dfs(int x){visit[x]=1;for (int i=head[x];i;i=e[i].next){int v=e[i].to;if (!visit[v]){deep[v]=deep[x]+1; dfs(v);f[x]+=f[v];sum[x]+=sum[v];}}f[x]++;sum[x]+=deep[x];} queue <pair<long long,long long> > que;int main(){int n;scanf("%d",&n);for (int i=1;i<n;i++){int u,v;scanf("%d%d",&u,&v);addedge(u,v);addedge(v,u);}deep[1]=1;dfs(1);memset(visit,0,sizeof(visit));que.push(make_pair(1,sum[1]));long long ans=sum[1];int num=1;visit[1]=true;while (!que.empty()){int x=que.front().first;long long y=que.front().second;que.pop();for (int i=head[x];i;i=e[i].next){if (!visit[e[i].to]){long long tot=y+(n-f[e[i].to])-f[e[i].to]; que.push(make_pair(e[i].to,tot));if (tot>ans){ans=tot;num=e[i].to;}if (tot==ans){if (e[i].to<num){num=e[i].to;}}visit[e[i].to]=true;}}}cout<<num;return 0;}

注意开long long