luogu P2680 运输计划 65分做法

Posted lovedsr

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了luogu P2680 运输计划 65分做法相关的知识,希望对你有一定的参考价值。

临近(noip,AK)不太现实,暴力才是王道,大佬无视

这里只介绍(65)分做法


(m==1) 的情况
很明显 就一条路径,当然要贪心选着一条路径路上的最大的边喽
傻逼分(get 20)

(n,m<=100)
想怎么暴力怎么暴力,反正不会TLE
枚举割哪一条边
枚举每条路径dfs()一边 寻找最大值
最后取min 就好了
复杂度(O(n^3))
又get 10分

很明显的二分,我就是不会(⊙o⊙)


一条链子的时候并且n<=3000
求路径的和的时候可以用前缀和维护一下
达到O(1)的查询
使得②的复杂度降低了一个(O(n))
最终复杂的(O(n^2))
get 15分 啦

一条链子的时候并且n>=3000
二分他的最大长度
当然得利用③的O(1)查询啦
考虑check函数
如果第i次运货路线大于x(也就是二分的mid)
那么要割掉的点一定在(l_{i})(r_{i})之间
维护一下左边界和右边界就好了(如果不成立的话直接return 0)
最后在左边界和右边界查询最大就好啦
最终复杂度O((nlogn)
get 20分

最终得分 (65)

打死我也不会lca

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=100007;
int n,m;
int S[maxn],T[maxn],num[maxn];
int noip[maxn],cz[maxn];
struct edge{
    int v,nxt,q;
}e[maxn<<1];
int head[maxn<<1],tot;
int a[maxn],sum[maxn];
int tot_30,max_30,flag_30;
void add_edge(int u,int v,int q)
{
    e[++tot].v=v;
    e[tot].q=q;
    e[tot].nxt=head[u];
    head[u]=tot;
}
inline int read()
{
    int x=0,f=1;char s=getchar();
    while(s<'0'||s>'9') {if(s=='-')f=-1;s=getchar();}
    while(s>='0'&&s<='9') {x=x*10+s-'0',s=getchar();}
    return x*f;
}
void dfs_30(int u,int f,int end)
{
    if(u==end) {
        flag_30=1;
        return;
    }
    for(int i=head[u];i;i=e[i].nxt)
    {   
        int v=e[i].v;
        if(v==f) continue;      

        int tmp=max_30;
        tot_30+=e[i].q;
        max_30=max(max_30,e[i].q);
        
        dfs_30(v,u,end);
        if(flag_30) return;

        tot_30-=e[i].q;
        max_30=tmp;
    }
}
int check(int x)
{
    int l=1,r=n;
    int zz=0;
    for(int i=1;i<=m;++i)
    {
        if(num[i] > x)
        {
            zz=max(zz,num[i]);
            if(l >= T[i]) return 0;
            if(r <= S[i]) return 0;
            l=max(l,S[i]);
            r=min(r,T[i]);
        }
    }
    int tmp=0;
    for(int i=l+1;i<=r;++i)
        tmp=max(tmp,a[i]);
    zz-=tmp;
    return zz > x ? 0 : 1;
}
int main()
{
    n=read(),m=read();
    int flag_lz=0;
    for(int i=1;i<n;++i)
    {
        int a=read(),b=read(),c=read();
        if(a==b+1||b==a+1) flag_lz++;
        add_edge(a,b,c);
        add_edge(b,a,c);
    }
    for(int i=1;i<=m;++i)
        S[i]=read(),T[i]=read();
    if(m==1)
    {
        dfs_30(S[1],0,T[1]);
        printf("%d
",tot_30-max_30);
        return 0;
    }
    if(n<=1000)
    {
        int ans=0x3f3f3f3f;
        for(int i=1;i<=2*n-2;i+=2) // 枚举航道
        {
            int tmp=e[i].q;
            e[i].q=e[i+1].q=0;
            int dsr=0;
            for(int j=1;j<=m;++j) //遍历客户
            {
                flag_30=tot_30=0;
                dfs_30(S[j],0,T[j]);
                dsr=max(dsr,tot_30);
            }
            ans=min(ans,dsr);
            e[i].q=e[i+1].q=tmp;        
        }
        printf("%d
",ans);
        return 0;
    }
    if(flag_lz==n-1)
    {
        for(int i=1,j=2;i<=2*(n-1);i+=2,j++)
            a[j]=e[i].q;
        for(int i=2;i<=n;++i)
            sum[i]=sum[i-1]+a[i];
        for(int i=1;i<=m;++i)
        {
            if(S[i]>T[i]) swap(S[i],T[i]);
            num[i]=sum[T[i]]-sum[S[i]];
        }
        int l=1,r=3e8;
        int ans;
        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;
    }
    return 0;
}

以上是关于luogu P2680 运输计划 65分做法的主要内容,如果未能解决你的问题,请参考以下文章

luogu P2680 运输计划

P2680 运输计划

洛谷——P2680 运输计划

P2680 运输计划

P2680 运输计划

P2680 运输计划[二分+LCA+树上差分]