信标

emmm树形dp?好像是的

搬一个题解证明过来

由于在n>1时答案至少为1,我们枚举一个必须放的根, 所有深度不同的点就被区分开了.

设一个节点有c个儿子, 发现必须在其中至少c−1个儿子的子树中放置信标.

证明如下: 考虑如果不这样放, 对于两棵都没有放的子树, 他们汇集到lca上以后距离都是相等的, 所以lca外的信标无法区分, 而内部没有信标. 所以不能存在两颗子树都不放. 所以至少要放c-1个. 由于在根节点放置了信标, 可以只考虑深度相同的点. 由于深度相同, 所以他们的lca度数至少为2,那么一定有一个信标在lca包含这两个点的两支子树中. 那么另一侧的点肯定要走更远的路, 会被区分开. 所以放c−1个足够区分.

这样问题变成每个节点要有c−1棵子树放有信标, 求最小方案. 直接贪心即可.

由于枚举根所 以复杂度为O(n2), 可以获得70分.

如何做到O(n)?

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

证明如下:

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

如果在不同的子树中, 有两种情况:

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

两个都在放了信标的子树中. 如果根的度数大于3则同上. 度数等于3时, 如果他们没有被区分开,一定是他们先汇集到了一个节点上, 然后走到同一个信标上. 这个点一定是一条奇链的中点, 且 不是根 (由于深度不同), 是在两个子树之一中唯一的. 那么他们走到另一个信标就一定有一个点走 了冤枉路, 既另一个信标可以区分出他们.

#include
#include
#include
#include
#define rint register int
template <class T>inline void read(T &X)
{
    X=0;int W=0;char ch=0;
    while(!isdigit(ch))W|=ch=='-',ch=getchar();
    while(isdigit(ch))X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    X=W?-X:X;return;
}
int n,head[1000010],cnt=0,count[1000010],f[1000010];
struct node{int to,next;}edge[2000010];

void add(int u,int v)
{
    edge[++cnt].to=v;
    edge[cnt].next=head[u];
    head[u]=cnt;
}

void dfs(int now,int fa)
{
    int num=0,tot=0;
    for(rint i=head[now];i;i=edge[i].next)
    {
        int to=edge[i].to;
        if(to==fa)continue;
        dfs(to,now);
        tot+=f[to];
        if(!f[to])num++;
    }
    f[now]=tot;
    if(num>1)f[now]+=(num-1);
    return;
}
int main()
{
    //freopen("beacon.in","r",stdin);
    //freopen("beacon.out","w",stdout);
    read(n);
    if(n==1){printf("0\n");return 0;}
    for(rint i=1;ii)
    {
        int u,v;
        read(u),read(v);
        add(u,v),add(v,u);
        ++count[u],++count[v];
    }
    int sta=-1;
    for(rint i=1;i<=n;++i)if(count[i]>=3)sta=i;
    if(sta==-1){printf("1\n");return 0;}
    dfs(sta,-1);
    printf("%d\n",f[sta]);
return 0;
}

 

转载于:https://www.cnblogs.com/pile8852/p/9892325.html

你可能感兴趣的:(信标)