bzoj2525 1426

来源:互联网 发布:android 网络测速代码 编辑:程序博客网 时间:2024/05/15 05:37

题面二分最短时间,求出最小的需要引爆数
至于关键点的引爆状态有关
f[i] 以i为根的子树中已经引爆的点离i最近的距离
g[i] 以i为根的子树中未引爆的点离i最远的距离
回溯到每个节点时,优先考虑用另一个儿子中的点覆盖其他儿子
if(f[i]+g[i]<=mid) g[i]=INF
if(g[i]==mid) 必须引爆x

#include <iostream>#include <cstdio>#include <cstring>#define  INF 1000000000#define maxn 300005using namespace std;int n,m;int a[maxn];int ans;struct edge{    int to,ne;}b[maxn*2];int k=0,head[maxn];int f[maxn],g[maxn];int limit;int num=0,op1=0;inline int read(){    int x=0,f=1;    char ch=getchar();    while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); }    while(ch>='0'&&ch<='9'){ x=x*10+ch-'0'; ch=getchar(); }    return x*f;}void add(int u,int v){    k++;    b[k].to=v; b[k].ne=head[u]; head[u]=k;}void dfs(int x,int pre){    f[x]=INF;    if(a[x]) g[x]=0;    else g[x]=-INF;    for(int i=head[x];i!=-1;i=b[i].ne)    if(b[i].to!=pre){        dfs(b[i].to,x);        f[x]=min(f[x],f[b[i].to]+1);        g[x]=max(g[x],g[b[i].to]+1);    }    if(g[x]+f[x]<=limit)  g[x]=-INF;    if(g[x]==limit){        f[x]=0,g[x]=-INF,num++;    }}bool check(int x){    if(!x) return op1<=m;    limit=x; num=0;    dfs(1,0);    //printf("%d %d %d %d\n",x,f[1],g[1],num);    if(g[1]+f[1]>limit) num++;    return num<=m;}void getans(){    int l=0,r=n,mid;    ans=n;    while(l<=r){        mid=(l+r)>>1;        if(check(mid)) ans=mid,r=mid-1;        else l=mid+1;    }}int main(){    //freopen("in.txt","r",stdin);    memset(head,-1,sizeof(head));    n=read(); m=read();    for(int i=1;i<=n;i++){        a[i]=read();        if(a[i]) op1++;    }    int x,y;    for(int i=1;i<n;i++)    {        x=read(); y=read();        add(x,y); add(y,x);    }    getans();    printf("%d\n",ans);    //while(1);    return 0;}

题面
考虑逆推
f[i] 已经买了i张邮票,距离n张邮票还需要的购买次数

f[i]=inf[i]+ninf[i+1]+1

f[i]=f[i+1]+nni

g[i]表示所需钱数
假设每张邮票需要一元,后面购买的邮票都会贵1元
g[i]=in(g[i]+f[i])+nin(g[i+1]+f[i+1])+1

原创粉丝点击