洛谷P1084 疫情控制(NOIp2012)

来源:互联网 发布:吊顶软件下载 编辑:程序博客网 时间:2024/05/16 02:37

贪心 倍增 二分

题目传送门

细节很多。。。改了老半天

首先二分答案。然后不断把军队往根节点靠(倍增实现)。如果达不到根节点就直接打个标记。达到根节点后存下来,同时存剩余的路程。

DFS求出哪些1的子树是没有被拦的。首先考虑放从同一节点上来的军队。如果已经用过,贪心的按照距离远的配剩下的路程多的来放。

具体见注释

代码:

#include<cstdio>#include<cstring>#include<algorithm>#define MAXN 50000using namespace std;typedef long long LL;struct edge{    int next,to; LL dis;}ed[MAXN*2+5];struct remain{ int x; LL v; }rem[MAXN+5],op[MAXN+5];//一个存军队,一个存子树int n,m,k;int h[MAXN+5],fa[MAXN+5][18],am[MAXN+5],flag[MAXN+5];LL dis[MAXN+5][18],rm[MAXN+5];bool f[MAXN+5],f1[MAXN+5];inline char readc(){//fread读优    static char buf[100000],*l=buf,*r=buf;    if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);    if (l==r) return EOF; return *l++;}inline LL _read(){    LL num=0; char ch=readc();    while (ch<'0'||ch>'9') ch=readc();    while (ch>='0'&&ch<='9') { num=num*10+ch-48; ch=readc(); }    return num;}bool srch(int x){//DFS判断子树是否被拦    if (f[x]) return true; bool fl=false;    for (int i=h[x];i;i=ed[i].next)        if (ed[i].to!=fa[x][0]){//有一个没有被拦就算没有被拦            fl=true;//说明有子树            if (!srch(ed[i].to))                 return false;        }    return fl;}bool cp(remain x,remain y){ return x.v>y.v; }bool pd(LL v){    int t=0,opn=0;     memset(f1,false,sizeof(f1));    memset(f,false,sizeof(f));    memset(flag,0,sizeof(flag));    for (int i=1;i<=m;i++){        int x=am[i];LL tt=v;        for (int j=17;j>=0;j--)            if (dis[x][j]<=tt&&fa[x][j]>1) tt-=dis[x][j],x=fa[x][j];        if (tt<dis[x][0]||fa[x][0]!=1) f[x]=true;//如果到达不了根节点        else{            rem[++t].v=tt-dis[x][0]; rem[t].x=i;            if (!flag[x]||rem[t].v<rm[x]) rm[x]=rem[t].v,flag[x]=i;        }    }    for (int i=h[1];i;i=ed[i].next)        if (!srch(ed[i].to)) op[++opn].v=ed[i].dis,op[opn].x=ed[i].to;//存起来    if (opn>t) return false; if (!opn) return true;    sort(rem+1,rem+t+1,cp); sort(op+1,op+opn+1,cp); int nd=1; f1[0]=true;    for (int i=1;i<=opn;i++){        if (!f1[flag[op[i].x]]) { f1[flag[op[i].x]]=true; continue; }//如果没有用过就用        while (nd<=t&&(f1[rem[nd].x]||rem[nd].v<op[i].v)) nd++;        if (nd>t) return false; f1[rem[nd].x]=true;    }    return true;}void dfs(int x){    for (int i=h[x];i;i=ed[i].next)        if (ed[i].to!=fa[x][0]){            int v=ed[i].to;            fa[v][0]=x; dis[v][0]=ed[i].dis; dfs(v);        }}void addedge(int x,int y,LL z){    ed[++k].next=h[x]; ed[k].to=y; ed[k].dis=z; h[x]=k;}void Make(){    for (int j=1;j<=17;j++)        for (int i=1;i<=n;i++){            fa[i][j]=fa[fa[i][j-1]][j-1];            dis[i][j]=dis[i][j-1]+dis[fa[i][j-1]][j-1];        }}int main(){    n=_read();    for (int i=1;i<n;i++){        int u=_read(),v=_read(); LL d=_read();        addedge(u,v,d); addedge(v,u,d);    }    dfs(1); Make(); m=_read();//倍增预处理    for (int i=1;i<=m;i++) am[i]=_read();    LL l=0,r=500000,ans=-1;//二分答案    while (l<=r){        LL mid=(l+r)/2;        if (pd(mid)) r=mid-1,ans=mid;        else l=mid+1;    }    return printf("%lld\n",ans),0;}
原创粉丝点击