[POI2008]Sta Bzoj1131

这题的n有点大,1000000的数据规模只能考虑O(n)解决(貌似跟O(nlogn)的算法扯不上关系)。

先假定1为根,这样能把这个有根图转成一棵以1为根的树。再用f[i],son[i]表示在这棵树中,以i为根节点下面最大的深度之和(深度是相对于整棵树,而不是i来说的),以及i节点下有多少个子节点(包括自己)。可以使用一个简单的广搜确定父节点后,用一个简单的动规累加,完成这步需要O(2n)的时间。

之后我们考虑换根。对于根从当前根节点的fa换到它的某个最近孩子u上的情况:u和它的所有孩子的深度都会-1,u另外一边的节点的深度都会+1,因此换完根之后的深度和就是f[fa]-son[u]+(n-son[u])。将根从1到n枚举一遍,看看哪个情况的深度之和最大,输出即可。

总复杂度O(n)。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
/**************************************************************
    Problem: 1131
    User: abcdabcd987
    Language: C++
    Result: Accepted
    Time:7432 ms
    Memory:64740 kb
****************************************************************/

#include <iostream>
using namespace std;

struct link {
  int v,next;
} g[3000000];
long long q[1000001],son[1000001],deep[1000001],father[1000001],f[1000001],n,top,ans,root;
bool s[1000001];
inline void join(const int x,const int y) {
  g[++top].v=y;
  g[top].next=g[x].next;
  g[x].next=top;
}
void bfs(const int source) {
  int head=0,tail=1;
  q[tail]=source;
  s[source]=true;
  father[source]=-1;
  deep[source]=1;
  while (head<tail) {
    int u=q[++head],v=g[u].next;
    son[u]=1;
    while (v!=-1) {
      if (!s[g[v].v]) {
        s[g[v].v]=true;
        father[g[v].v]=u;
        q[++tail]=g[v].v;
        deep[tail]=deep[head]+1;
      }
      v=g[v].next;
    }
  }
  for (int i=n;i>1;--i) {
    f[q[i]]+=deep[q[i]];
    son[father[q[i]]]+=son[q[i]];
    f[father[q[i]]]+=f[q[i]];
  }
}
int main() {
  ios::sync_with_stdio(false);
  cin>>n;
  for (int i=1;i<=n;++i) g[i].next=-1;
  top=n;
  for (int i=1,x,y;i<n;++i) {
    cin>>x>>y;
    join(x,y);
    join(y,x);
  }
  bfs(1);
  ans=f[root=1];
  for (int i=2;i<=n;++i) {
    long long u=q[i],fa=father[u];
    f[u]=f[fa]-son[u]+n-son[u];
    if ((f[u]==ans&&u<root) || (f[u]>ans)) {
      root=u;
      ans=f[u];
    }
  }
  cout<<root;
}

Comments