zoukankan      html  css  js  c++  java
  • 洛谷P1084 疫情控制(NOIP2012)(二分答案,贪心,树形DP)

    洛谷题目传送门

    费了几个小时杠掉此题,如果不是那水水的数据的话,跟列队的难度真的是有得一比。。。

    话说蒟蒻仔细翻了所有的题解,发现巨佬写的都是倍增,复杂度是(O(nlog nlog nw))的,貌似还不够优秀。

    其实我们与其对于每一个点都通过倍增向上找到对应位置,还不如直接从上到下dfs一遍,判断:如果当前点子树内初始位置最浅的军队与当前点距离不超过(mid),或者所有子树都被封锁,那么当前点也被封锁。

    这样以后再二分,时间复杂度降至(O(nlog nw))。其它部分的思路Dalao们的题解里都讲清楚了,蒟蒻也不多说了qwq

    #include<bits/stdc++.h>
    #define LL long long
    #define RG register
    #define R RG int
    using namespace std;
    const int N=5e4+9,M=1e5+9;
    int p,he[N],ne[M],to[M],w[M],top[N],at[N],t[N],mn[N];
    LL mid,d[N],dis[N];
    bool cov[N],use[N];
    inline bool cmp(R x,R y){
        return d[x]>d[y];
    }
    void dp(R x,R f){
        top[x]=p;
        if(mid<d[x])mid=d[x];//控制二分上界
        if(to[he[x]]==f)he[x]=ne[he[x]];//悄悄把反边删掉
        for(R i=he[x];i;i=ne[i]){
            if(to[ne[i]]==f)ne[i]=ne[ne[i]];
            d[to[i]]=d[x]+w[i];
            dp(to[i],x);
        }
    }
    void dfs(R x){
        if(cov[x]){dis[x]=0;return;}
        dis[x]=1ll<<60;
        if(!(cov[x]=he[x]))return;//到达叶子节点还没有被封锁
        for(R y,i=he[x];i;i=ne[i]){
            dfs(y=to[i]);
            cov[x]&=cov[y];
            dis[x]=min(dis[x],dis[y]+w[i]);
        }
        if(dis[x]<=mid)cov[x]=1;//子树内军队能够赶到
    }
    int main(){
        R n,m,cnt=0,i,u,v;
        cin>>n;
        for(i=1;i<n;++i){
            cin>>u>>v>>w[++p];w[p+1]=w[p];
            ne[p]=he[u];to[he[u]=p]=v;++p;
            ne[p]=he[v];to[he[v]=p]=u;
        }
        for(i=he[1];i;i=ne[i])
            d[p=to[i]]=w[i],dp(t[++cnt]=to[i],1);
        cin>>m;
        if(cnt>m)return cout<<"-1"<<endl,0;//无解
        for(i=1;i<=m;++i)cin>>at[i];
        sort(t+1,t+cnt+1,cmp);//排序,方便接下来贪心
        sort(at+1,at+m+1,cmp);
        RG LL l=0,r=mid+d[t[1]];
        while(l<r){
            mid=(l+r)>>1;
            memset(cov,0,n+1);
            memset(use,0,n+1);
            for(i=1;i<=cnt;++i)mn[t[i]]=0;//清空
            for(i=1;d[at[i]]>mid;++i)
                cov[at[i]]=1;//到不了根节点,直接留在子树内
            for(p=i;i<=m;++i)
                if(!mn[top[at[i]]])mn[top[at[i]]]=i;
                //每个子树预留一个贡献最小的军队
            use[0]=1;u=m+1;
            for(i=1;i<=cnt;++i){
                dfs(t[i]);
                if(cov[t[i]])continue;
                if(use[mn[t[i]]]){//预留已用,只好拿其它子树的
                    for(--u;u>=p&&(d[at[u]]+d[t[i]]>mid||use[u]);--u);
                    if(u<p)break;
                    use[u]=1;
                }
                else use[mn[t[i]]]=1;//预留直接用
            }
            u>=p?r=mid:l=mid+1;
        }
        cout<<l<<endl;
        return 0;
    }
    
  • 相关阅读:
    store的应用
    创建简单的SimpleStore
    设置间隔一周的算法
    C#里缓存的使用
    javaFileWrite,FileReader
    javaArrayList
    java函数方法
    java学生管理系统(简易版)
    Tensorflow框架
    java日期
  • 原文地址:https://www.cnblogs.com/flashhu/p/9704153.html
Copyright © 2011-2022 走看看