• 【Luogu】P2680运输计划(树上差分+二分)


      题目链接

      总体思路……怎么说呢……是个暴力吧……

      首先用倍增预处理出每条路径的长度。  然后按长度把路径排序。

      然后二分答案。对于当前答案mid检验,怎么检验呢?

      首先差分把所有长度比mid大的链上除了LCA之外的所有点权+1。dfs求出每个点的点权,顺便记下有多少点是被所有路径经过的。对于这些点,如果有一个点使得它到他的父亲那条边被删掉后,长度最长的路径都能卡过mid,那么返回true.

      那么检查所有点发现长度最长的路径卡不过mid,那么返回false。

      然后这题搞完了。

      

    #include<cstdio>
    #include<cstdlib>
    #include<cctype>
    #include<cstring>
    #include<algorithm>
    
    using namespace std;
    int n,m;
    
    inline int read(){
        int num=0,f=1;
        char ch=getchar();
        while(!isdigit(ch)){
            if(ch=='-')    f=-1;
            ch=getchar();
        }
        while(isdigit(ch)){
            num=num*10+ch-'0';
            ch=getchar();
        }
        return num*f;
    }
    
    struct Edge{
        int next,to,dis;
    }edge[1000010];
    int head[1000100],num;
    inline void add(int from,int to,int dis){
        edge[++num]=(Edge){head[from],to,dis};
        head[from]=num;
    }
    struct Node{
        int from,to,dis,lca;
        bool operator <(const Node &a)const{
            return dis>a.dis;
        }
    }q[300020];
    int s[400020][20],d[400020][20];
    int deep[400020];
    int vis[300020],e[300020];
    int pos[1001001],tot;
    int Max,ans;
    
    void find(int x,int fa){
        deep[x]=deep[fa]+1;
        for(int i=head[x];i;i=edge[i].next){
            int to=edge[i].to;
            if(to==fa)    continue;
            s[to][0]=x;d[to][0]=edge[i].dis;find(to,x);
        }
    }
    
    void dfs(int x,int fa,int size){
        e[x]=vis[x];
        for(int i=head[x];i;i=edge[i].next){
            int to=edge[i].to;
            if(to==fa)    continue;
            dfs(to,x,size);
            e[x]+=e[to];
        }
        if(e[x]==size)    pos[++tot]=x;
    }
    
    bool check(int limit){
        memset(vis,0,sizeof(vis));
        int size=0;tot=0;
        for(int i=1;i<=m;++i){
            if(q[i].dis<=limit)    break;
            vis[q[i].from]++;vis[q[i].to]++;vis[q[i].lca]-=2;
            size++;
        }
        dfs(1,1,size);
        for(int i=1;i<=tot;++i)
            if(q[1].dis-d[pos[i]][0]<=limit)    return 1;
        return 0;
    }
    
    int main(){
        n=read(),m=read();
        for(int i=1;i<n;++i){
            int from=read(),to=read(),dis=read();
            add(from,to,dis);
            add(to,from,dis);
        }
        for(int i=1;i<=m;++i)    q[i]=(Node){read(),read(),0,0};
        find(1,1);
        for(int j=1;j<20;++j)
            for(int i=1;i<=n;++i){
                s[i][j]=s[s[i][j-1]][j-1];
                d[i][j]=d[i][j-1]+d[s[i][j-1]][j-1];
            }
        for(int i=1;i<=m;++i){
            int from=q[i].from,to=q[i].to;    int &dis=q[i].dis,&lca=q[i].lca;
            if(deep[from]<deep[to])    swap(from,to);
            int f=deep[from]-deep[to];
            for(int j=0;(1<<j)<=f;++j)
                if(f&(1<<j)){
                    dis+=d[from][j];
                    from=s[from][j];
                }
            if(from==to){
                lca=from;
                Max=max(Max,dis);
                continue;
            }
            for(int j=19;j>=0;--j){
                if(s[from][j]==s[to][j])    continue;
                dis+=d[from][j]+d[to][j];
                from=s[from][j];to=s[to][j];
            }
            dis+=d[from][0]+d[to][0];
            lca=s[from][0];
            Max=max(Max,dis);
        }
        sort(q+1,q+m+1);
        int l=0,r=Max;
        while(l<=r){
            int mid=(l+r)>>1;
            if(check(mid)){    ans=mid;r=mid-1;    }
            else        l=mid+1;
        }
        printf("%d",ans);
        return 0;
    }
  • 相关阅读:
    Linux 进程间通信(一)(经典IPC:消息队列、信号量、共享存储)
    Linux 进程间通信(一)(经典IPC:管道、FIFO)
    Linux I/O 进阶
    Linux 信号
    Linux 进程(二):进程关系及其守护进程
    转载:什么是B+树?
    转载:什么是B树?
    Redis批量删除脚本
    Java时间工具类
    JSP2.2自定义标签、EL函数
  • 原文地址:https://www.cnblogs.com/cellular-automaton/p/7802244.html
Copyright © 2020-2023  润新知