zoukankan      html  css  js  c++  java
  • P1084 疫情控制

    P1084 疫情控制

    好像二分、倍增、树上差分是比较热门的考

    会结合在一起考,难度比较大,需要多加练习。

    现在在解决noip最后的几道大题,很鹅心。也没有人做向导,很难受qwq


    首先这是一棵树,一个军队肯定是越往上走越好。(有大佬说过,对于这种提点的题,要是用倍增)

    要是时间最短,就是要是用时最长的军队用时最短,使用二分

    然后对于一个走不到首都的军队来说,也肯定是所处深度越小越好

    对于可以到达首都的军队来说(不在其子树停留,不过要保存其在那颗子树),我们先通过dfs遍历一遍,找出还有那些子树没有被封锁,再考虑派军队过去,到根节点的儿子,讲这一颗子树封锁

    然后我们贪心的派军队。用时小的路径,拍剩余时间少的军队。如果剩余时间最少的军队不能过去,则让其返回其子树的根节点。

    如果如此操作之后,仍有子树没有被封锁,则需要加大时间

    #include<cstdio>
    #include<algorithm>
    #include<iostream>
    #include<cstring>
    using std::sort;
    const int maxn=50100;
    int n,m;
    struct node
    {
        int p;
        int w;
        int nxt;
    };
    struct Data
    {
        int n1;
        int n2;
    };
    Data need[maxn],army[maxn];
    node line[maxn<<1];
    int head[maxn],tail;
    int f[maxn][22];
    int dis[maxn][22];
    int nt,at;
    int stop[maxn];
    int pos[maxn];
    int son[maxn];
    void add(int a,int b,int c)
    {
        line[++tail].p=b;
        line[tail].w=c;
        line[tail].nxt=head[a];
        head[a]=tail;
    }
    int read()
    {
        int res=0;
        char c=getchar();
        while(c>'9'||c<'0')	c=getchar();
        while(c>='0'&&c<='9')
        {
            res=(res<<3)+(res<<1)+c-'0';
            c=getchar();
        }
        return res;
    }
    void dfs(int now,int fa,int d)
    {
        for(int i=1;(1<<i)<=d;i++)
        {
            f[now][i]=f[f[now][i-1]][i-1];
            dis[now][i]=dis[now][i-1]+dis[f[now][i-1]][i-1];
        }
        for(int i=head[now];i;i=line[i].nxt)
            if(line[i].p!=fa)
            {
                f[line[i].p][0]=now;
                dis[line[i].p][0]=line[i].w;
                dfs(line[i].p,now,d+1);
            }
    }
    bool compare(const Data &a,const Data &b)
    {
        return a.n1<b.n1;
    }
    bool Dfs(int now,int fa)
    {
        if(stop[now])	return true;
        int son=0;
        for(int i=head[now];i;i=line[i].nxt)
            if(line[i].p!=fa)
            {
                son++;
                if(!Dfs(line[i].p,now))	
                    return false;
            }
        if(!son)	return false;
        return true;
    }
    bool check(int t)//t是最长的时间
    {
        nt=at=0;
        memset(stop,false,sizeof(stop));//stop为节点是否被封锁的标记
        for(int i=1;i<=m;i++)
        {
            int used=0,now=pos[i];
            for(int j=19;j>=0;j--)
                if(f[now][j]&&f[now][j]!=1&&used+dis[now][j]<=t)//向上提点
                {
                    used+=dis[now][j];
                    now=f[now][j];
                }
            if(f[now][0]==1&&t-used-son[now]>=0)//可以到达首都
            {
                army[++at].n1=t-used-son[now];//存储下来
                army[at].n2=now;//子树也要存下来,以便下面返回无法调度的军队
            }
            else
                stop[now]=true;
        }
        for(int i=head[1];i;i=line[i].nxt)
            if(!Dfs(line[i].p,1))//判断一个子树是否被完全封锁
            {
                need[++nt].n1=son[line[i].p];
                need[nt].n2=line[i].p;//如果没有,存下来
            }
            else
                stop[line[i].p]=true;
        sort(army+1,army+1+at,compare);//排个序
        sort(need+1,need+1+nt,compare);
        int i=1,j=0;
        for(;i<=nt;i++)
        {
            if(stop[need[i].n2])	continue;//如果这个子树被之前退回的军队封锁
            for(j+=1;j<=at;j++)
            {
                if(need[i].n1>army[j].n1)//时间不够了,我们就不能让他待在首都,退回去
                    stop[army[j].n2]=true;//顺便将退回去的子树封锁了
                if(stop[need[i].n2]||need[i].n1<=army[j].n1)//当前子树已经被封锁了,若找到了一个可以派遣的剩余时间最少的军队
                {
                    stop[need[i].n2]=true;//打上封锁标记
                    break;
                }
            }
            if(j==at+1)	return false;//军队用完了,而还有子树没有被封锁
        }
        return true;
    }
    int main()
    {
        n=read();
        int a,b,c;
        for(int i=1;i<n;i++)
        {
            /*scanf("%d%d%d",&a,&b,&c);*/
            a=read(),b=read(),c=read();
            add(a,b,c);add(b,a,c);
        }
        dfs(1,0,1);//预处理倍增
        m=read();
        for(int i=1;i<=m;i++)
            pos[i]=read();
        int l=0,r=200000;
        for(int i=head[1];i;i=line[i].nxt)
            son[line[i].p]=line[i].w;//先预处理出来从根到其子树的距离,其实没有什么用qwq
        while(l<r)//二分答案
        {
            int mid=(l+r)>>1;
            if(check(mid))
                r=mid;
            else
                l=mid+1;
        }
        if(l!=200000)	printf("%d",l); 
        else	printf("-1");
        return 0;
    }
    
  • 相关阅读:
    【Oracle】求历史表以往记录中最接近给定时间的一批记录
    【Oracle】用check语法限制字段的输入值
    【Oracle】行转列
    【Thymeleaf】如何实现including
    【Javascript】JS dom设置元素的css样式
    MS SQL BackUp Database && Shrink DB Log && SP WHO LOCK
    PHP magic_quotes_gpc的详细使用方法
    [轉載]用PHP的ob_start();控制您的浏览器cache!
    [轉]jQuery选择器 – 属性过滤(Attribute Filters)
    [轉]CSS之自动换行
  • 原文地址:https://www.cnblogs.com/Lance1ot/p/9401784.html
Copyright © 2011-2022 走看看