P3554 [POI2013]LUK-Triumphal arch

题目链接

首先,我们可以发现这题的可行方案具有单调性,所以可以考虑用 二分 + O ( n ) \mathcal O(n) O(n) check 来寻找最优答案

显然,我们只需要考虑从根往下走的情况下,也就是不需要考虑回头
因为在回溯的时候可以“无压力”地染色,回溯后再往下走 肯定比 直接往下走的代价小。所以,如果我们已经计算过了所有直接往下走的代价,就已经覆盖了所有的答案。

f i f_i fi 表示 i i i 的子树内(不包括 i i i)需要祖先来染色的节点个数,也就是说这些节点在 B 走到 i i i 的祖先的时候就需要被染色
k k k 为当前二分到的,一次操作可以染色的节点个数

转移方程: f x = { ∑ f y } + d x − k f_x=\lbrace\sum f_y \rbrace+d_x-k fx={ fy}+dxk
其中 d x d_x dx x x x 的子节点个数, y y y x x x 的儿子

在求出 f f f 数组后,如果 f 1 = 0 f_1=0 f1=0 说明可行,否则不可行

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int Maxn=300010;
const int Maxm=Maxn<<1;
int nxt[Maxm],to[Maxm];
int head[Maxn],d[Maxn],f[Maxn];
int n,val,ans,edgecnt;
inline int read()
{
    
    
    int s=0,w=1;
    char ch=getchar();
    while(ch<'0'||ch>'9'){
    
    if(ch=='-')w=-1;ch=getchar();}
    while(ch>='0' && ch<='9')s=(s<<3)+(s<<1)+(ch^48),ch=getchar();
    return s*w;
}
inline void add(int x,int y)
{
    
    
    ++edgecnt;
    nxt[edgecnt]=head[x];
    to[edgecnt]=y;
    head[x]=edgecnt;
}
void dfs(int x,int fa)
{
    
    
    int tot=d[x]-val;
    for(int i=head[x];i;i=nxt[i])
    {
    
    
        int y=to[i];
        if(y==fa)continue;
        dfs(y,x),tot+=f[y];
    }
    f[x]=max(0,tot);
}
bool check()
{
    
    
    memset(f,0,sizeof(f));
    dfs(1,0);
    return f[1]==0;
}
int main()
{
    
    
    // freopen("in.txt","r",stdin);
    n=read();
    for(int i=1;i<n;++i)
    {
    
    
        int x=read(),y=read();
        add(x,y),add(y,x);
        ++d[x],++d[y];
    }
    for(int i=2;i<=n;++i)
    --d[i];
    int l=0,r=n-1;
    while(l<r)
    {
    
    
        int mid=(l+r)>>1;val=mid;
        if(check())r=mid;
        else l=mid+1;
    }
    printf("%d\n",l);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Brian_Pan_/article/details/112513081
今日推荐