1 solutions

  • 0
    @ 2025-3-7 16:38:00

    nlognlognnlognlogn

    性质1:除了1号点之外,一定存在一组最优解,使得军队不往下移动。且军队往上移动越多越好。

    性质2:答案具有二段性,所以可以用二分, fa[ij]fa[i,j]表示从ii点开始往上跳2j2^j步后,可以跳到哪个点。

    dist[i]dist[i]表示从根节点走到节点ii的距离是多少。

    (1)对于无法走到根节点的军队,能走到哪就让它走到哪。统计一下还有哪些根节点的子树没有被完全覆血,记没有被覆盖的集合为A。

    (2)对于剩余军队呢,首先一定要先走到根节点的儿子上。把这些军队记为集合B。他们有两种选择:原地驻扎或者先走到根节点再走到其他城市。

    性质3:考虑走到了A集合中某个点的所有军队中剩余时间最少的军队(其实也就是根节点的孩子节点),如果这支军队无法走到首都再走回来,那么必然存在一组最优解,使得当前军队原地驻扎.

    去掉原地驻扎的军队,然后使用这些军队去覆盖掉根节点的孩子节点之中没有覆盖到的点即可.

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long LL;
    const int N=50010,M=N*2,K=16;
    const LL INF=5e13;
    int n,m;
    int h[N],e[M],w[M],ne[M],idx;
    int fa[N][K],q[N],p[N];
    LL dist[N]; //dist[u]表示到根节点的距离
    bool st[N],g[N]; //st[i]=1 表示已经有军队了,g[i]=1 表示i节点的子树已经被完全覆盖了
    
    struct Army{ //军队
        int ver;
        LL rest;
        bool is_del; //是否删除
    }army[N];
    
    void add(int a,int b,int c) //添加点
    {
        e[idx]=b,w[idx]=c,ne[idx]=h[a],h[a]=idx++;
    }
    void dfs_fa(int u,int father,LL distance) //预处理往上跳
    {
        dist[u]=distance; //当前距离
        for(int i=h[u];i!=-1;i=ne[i]) //枚举当前节点的子节点
        {
            int j=e[i];
            if(j==father) continue;  //父节点不用管
            fa[j][0]=u;
            for(int k=1;k<K;k++) //枚举走的步数
            {
                fa[j][k]=fa[fa[j][k-1]][k-1];
            }
            dfs_fa(j,u,distance+w[i]); //继续往下递归计算
        }
    }
    bool is_leaf(int u) //判断u是否为叶节点
    {
        return ne[h[u]]==-1; 
    }
    void dfs_g(int u,int father,bool has_army) //判断当前节点是否被覆盖
    {
        has_army|=st[u]; //判断是否有军队
        if(is_leaf(u)) g[u]=has_army; //叶子节点
        else //遍历当前节点的子节点
        {
            g[u]=true; //默认可以
            for(int i=h[u];i!=-1;i=ne[i]) //搜索子节点
            {
                int j=e[i];
                if(j==father) continue;
                dfs_g(j,u,has_army);
                if(!g[j]) g[u]=false; //子节点不能够被覆盖,自己也就不能被覆盖
            }
        }
    }
    bool check(LL mid) //判断时间为mid的时候,是否可以完全覆盖
    {
        memset(st,0,sizeof st); //清空
        int cnt=0;
        for(int i=0;i<m;i++)
        {
            int j=q[i]; //第i个军队
            for(int k=K-1;k>=0;k--)
            {
                if(fa[j][k]>1&&dist[q[i]]-dist[fa[j][k]]<=mid) //可以跳
                {
                    j=fa[j][k];
                }
            }
            if(dist[q[i]]>mid) st[j]=true; //走不到根节点
            else army[cnt++]={j,mid-(dist[q[i]]-dist[j]),false}; //存下可以跳的点
            //                编号和剩余时间
        }
        memset(g,0,sizeof g); //子树是否完全覆盖
        dfs_g(1,-1,false); //搜索根节点里面没有覆盖的点
        memset(p,-1,sizeof p); //记录每个城市剩余时间最少的军队
        for(int i=0;i<cnt;i++) //枚举所有军队
        {
            int ver=army[i].ver; //节点编号
            if(p[ver]==-1||army[p[ver]].rest>army[i].rest)
            {
                p[ver]=i;
            }
        }
        for(int i=h[1];i!=-1;i=ne[i]) //枚举根节点里的每个城市
        {
            int j=e[i];
            if(!g[j]) //不能被覆盖
            {
                int k=p[j];
                
                if(k!=-1&&army[k].rest<dist[army[k].ver]*2) //不能走到根再走回来
                {
                    army[k].is_del=true; //原地驻扎
                    g[j]=true;
                }
            }
        }
        int ncnt=0;
        for(int i=0;i<cnt;i++) //删除了原地驻扎以后的军队
        {
            if(!army[i].is_del)
            {
                army[ncnt++]=army[i];
            }
        }
        int gcnt=0;
        for(int i=h[1];i!=-1;i=ne[i]) //新的没有覆盖的军队
        {
            int j=e[i];
            if(!g[j])
            {
                p[gcnt++]=j;
            }
        }
        sort(army,army+ncnt,[&](Army &a,Army &b){ //军队按照走到根节点的剩余时间从小到大排序
            return a.rest-dist[a.ver]<b.rest-dist[b.ver];
        });
        sort(p,p+gcnt,[&](int a,int b){ //根节点没有走到城市按照时间从小到大排序
            return dist[a]<dist[b];
        });
        int k=0;
        for(int i=0;i<ncnt&&k<gcnt;i++) //贪心覆盖
        {
            if(army[i].rest-dist[army[i].ver]>=dist[p[k]])
            {
                k++;
            }
        }
        return k==gcnt;
    }
    int main()
    {
        cin>>n;
        memset(h,-1,sizeof h);
        for(int i=0;i<n-1;i++) //城市网络
        {
            int a,b,c;
            cin>>a>>b>>c;
            add(a,b,c);
            add(b,a,c);
        }
        dfs_fa(1,-1,0); //预处理走法
        cin>>m;
        for(int i=0;i<m;i++) //获取每个军队
        {
            cin>>q[i];
        }
        LL l=0,r=INF; //二分
        while(l<r)
        {
            LL mid=l+r>>1;
            if(check(mid)) r=mid;
            else l=mid+1;
        }
        if(r==INF) r=-1; //无法覆盖
        cout<<r;
        return 0;
    }
    
    • 1

    Information

    ID
    1099
    Time
    1000ms
    Memory
    256MiB
    Difficulty
    10
    Tags
    # Submissions
    1
    Accepted
    1
    Uploaded By