弱省高二老年划水选手在此!!!!!!!!(目前已退役)

Ryan_

Every thing that kills me makes me feel alive.

题解 luoguP3554 【[POI2013]LUK-Triumphal arch】

代码的关键部分

inline void dfs(int u,int fa) {
    int sum=0;
    for(int i=first[u]; i; i=nxt[i]) {
        int v=go[i];
        if(v==fa)continue;
        dfs(v,u);
        sum+=f[v]+1;
    }
  f[u]=max(sum-mid,0);

}

关于这个方程解释一下

f[u]=max(sum-mid,0);

我们不断搜索去边,然后回溯更新每一个阶段需要被染色的数量;

假设需要被染色的节点数量不足k,即sum-mid<0,那我们取0,原因是每次染色的机会是从后往前推的,即使仍然有染色的机会也不能更新前面的状态;

而如果需要染色的节点数量大于k,即sum-mid>0,那么我们显然可以累加到父亲节点,在回溯到父亲节点的时候再统一处理,因为在B点在父亲时是可以对儿子节点进行染色的(前提是sum-mid>0,即仍有染色次数),此处区别于上面那种情况;

所以我们每次二分mid

如果f[1]=0,显然是合法的,我们就缩小mid

AC code:

#include<bits/stdc++.h>
using namespace std;
const int N=2000005;
int first[N],nxt[N],go[N],tot,f[N],mid;
void add(int x,int y) {
    nxt[++tot]=first[x];
    first[x]=tot;
    go[tot]=y;
}
inline void dfs(int u,int fa) {
    int sum=0;
    for(int i=first[u]; i; i=nxt[i]) {
        int v=go[i];
        if(v==fa)continue;
        dfs(v,u);
        sum+=f[v]+1;
    }
    f[u]=max(sum-mid,0);
}
int main() {
    int n;
    scanf("%d",&n);
    if(n==1)printf("0"),exit(0);
    for(int i=1,x,y; i<n; i++) {
        scanf("%d%d",&x,&y);
        add(x,y);
        add(y,x);
    }
    int l=1,r=1<<30,ans=0;
    while (l<r) {
        memset(f,0,sizeof(f));
        mid=(l+r)>>1;
        dfs(1,0);
        if (!f[1])r=mid;
        else l=mid+1;
    }
    printf("%d\n",r);
    return 0;
}
View Code

 

posted @ 2019-10-16 12:29  Ryan_zero  阅读(113)  评论(0编辑  收藏  举报