[DFS][贪心]JZOJ 5944 信标

Description

 

Input

第一行一个正整数 n.
接下来 n − 1 行每行两个个正整数 u, v, 表示有一条连接 u, v 的道路.

Output

一行, 表示最少需要的信标数量.
 

Sample Input

4
1 2
1 3
1 4

Sample Output

2

样例 1 解释
在村庄 3, 4 放置信标, 四个村庄接收到的定位序列分别为 [1, 1], [2, 2], [0, 2], [2, 0].
如果只在 3 放置, 则村庄 2, 4 接收到的定位序列都是 [2], 不满足要求.
注意定位序列是有序的.
 

Data Constraint

样例 2
见下发文件中的 ex_beacon2.in/out.
 

Hint

对于前 20% 的数据, n ≤ 10;
对于前 45% 的数据, n ≤ 40, 树的形态随机;
对于前 70% 的数据, n ≤ 5000;
对于另 5% 的数据, 不存在一个村庄连接着 3 条或以上的道路;
对于 100% 的数据, 1 ≤ n ≤ 1000000, 1 ≤ u, v ≤ n, 保证数据合法.

分析

 我们先特判链的情况答案为 1, 然后找到任意一个度数大于 2 的节点, 可以证 明这个点一定不需要放置信标.

于是以这个点作根 O(n) 的贪心即可.

证明如下:

深度相同的点对证明同上, 只考虑深度不同的点对. 如果它们在一颗子树中, 由于度数大于 2 所 以一定有另一颗子树的一个信标把他们区分开.

如果在不同的子树中, 有两种情况: 一个在没放信标的子树中, 一个在放了的子树中.

显然还存在另一个子树放了信标, 由于深度不 同他们会被这个信标区分开.

两个都在放了信标的子树中. 如果根的度数大于 3 则同上. 度数等于 3 时, 如果他们没有被区分开, 一定是他们先汇集到了一个节点上, 然后走到同一个信标上.

这个点一定是一条奇链的中点, 且不是根 (由于深度不同), 是在两个子树之一中唯一的. 那么他们走到另一个信标就一定有一个点走 了冤枉路, 既另一个信标可以区分出他们.

#include <iostream>
#include <cstdio>
using namespace std;
const int N=1e6+10;
struct Edge {
    int u,v,nx;
}g[2*N];
int cnt,list[N];
int deg[N],a[N];
int n;

void Add(int u,int v) {
    g[++cnt].u=u;g[cnt].v=v;g[cnt].nx=list[u];list[u]=cnt;
}

void Dfs(int u,int fa) {
    int c=0;
    for (int i=list[u];i;i=g[i].nx)
        if (g[i].v!=fa) {
            Dfs(g[i].v,u);
            a[u]+=a[g[i].v];
            if (!a[g[i].v]) c++;
        }
    a[u]+=c-(c!=0);
}

int main() {
    freopen("beacon.in","r",stdin);
    freopen("beacon.out","w",stdout);
    scanf("%d",&n);
    if (n==1) {
        printf("0");
        return 0;
    }
    for (int i=1;i<n;i++) {
        int u,v;
        scanf("%d%d",&u,&v);
        Add(u,v);Add(v,u);
        deg[u]++;deg[v]++;
    }
    int root=0;
    for (int i=1;i<=n;i++)
        if (deg[i]>2) {
            root=i;
            break;
        }
    if (!root) {
        printf("1");
        return 0;
    }
    Dfs(root,0);
    printf("%d",a[root]);
}
View Code

 

posted @ 2018-11-03 16:56  Vagari  阅读(174)  评论(0编辑  收藏  举报