NOIP2015day2 运输计划(二分答案+差分/抽路+前缀最大值)

版权声明:我这种蒟蒻的文章,真正的大佬一般看不上的_(:з」∠)_ https://blog.csdn.net/Paulliant/article/details/82864697

题意

https://www.luogu.org/problemnew/show/P2680

思路

最大路径最短,二分吧。。。然后就二分了,考试时 95 95 分,常数太大,边界设的太大。
二分的边界如果设的小,能大大减小常数,对于这道题,最长时间肯定不会长过最长路径的值,短过最长路径减整棵树上的最长边。这样算一下顶多二分 10 10 次,血赚。
二分的 check \text{check} 也比较清晰,对于一个给定的最短路最大值 k k ,将 m m 条路径中长度超过 k k 的都标记掉,考虑清掉一条被所有路径覆盖的边的权值,如果存在清掉某条边的权值,能使最长不合法的路径长度不超过 k k ,那么这个 k k 合法,尝试更小的 k k
但是 dfs \text{dfs} 的常数很大,导致在比较慢的测评机上 TLE \text{TLE} ,如果把 dfs \text{dfs} 标记差分改成跳重链标记,看上去像多了一个 log \log ,实际上少了一个大常数,记住跳重链常数很小。
仔细观察,如果清边不删在最长的给定路径上的话,就没什么意义了,由此想到“抽出”最长路径,然后将子树挂在下面,这也是树的一个很好的结构。
如果清除了某条边的权值,不难发现只要对这条边左边子树的最长路、右边子树的最长路、清完边后拉出最长路的长度取 max \max 即可,所以在拉出路径后维护一个前缀、后缀最大值。

代码

二分答案+差分
#include<bits/stdc++.h>
#define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i)
#define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i)
typedef long long LL;
using namespace std;
const int N=3e5+3;
template<const int maxn,const int maxm>struct Linked_list
{
    int head[maxn],to[maxm],cost[maxm],nxt[maxm],tot;
    void clear(){memset(head,-1,sizeof(head));tot=0;}
    void add(int u,int v,int w){to[++tot]=v,cost[tot]=w,nxt[tot]=head[u],head[u]=tot;}
    #define EOR(i,G,u) for(int i=G.head[u];~i;i=G.nxt[i])
};
Linked_list<N,N<<1>G;
int dep[N],fa[N],sz[N],son[N],top[N],ed[N],dis[N],dfn[N],ord,dif[N];
int X[N],Y[N],lca[N],Pt[N],n,m,maxer,maxed;
 
void dfs(int u,int f,int d,int D)
{
    dep[u]=d,fa[u]=f,sz[u]=1,son[u]=0,dis[u]=D;
    EOR(i,G,u)
    {
        int v=G.to[i],w=G.cost[i];
        if(v==f)continue;
        ed[v]=w;
        dfs(v,u,d+1,D+w);
        sz[u]+=sz[v];
        if(sz[v]>sz[son[u]])son[u]=v;
    }
}
void make_path(int u,int f,int tp)
{
    dfn[u]=++ord,top[u]=tp;
    if(son[u])make_path(son[u],u,tp);
    EOR(i,G,u)
    {
        int v=G.to[i];
        if(v==f||v==son[u])continue;
        make_path(v,u,v);
    }
}
int LCA(int x,int y)
{
    while(top[x]!=top[y])
    {
        if(dep[top[x]]>dep[top[y]])x=fa[top[x]];
        else y=fa[top[y]];
    }
    return dep[x]<dep[y]?x:y;
}
void update(int x,int y)
{
    while(top[x]!=top[y])
    {
        if(dep[top[x]]<dep[top[y]])swap(x,y);
        dif[dfn[top[x]]]++,dif[dfn[x]+1]--;
        x=fa[top[x]];
    }
    if(dep[x]<dep[y])swap(x,y);
    dif[dfn[y]+1]++,dif[dfn[x]+1]--;
}
bool check(int k)
{
    int cnt=0,maxcut=0;
    memset(dif,0,sizeof(dif));
    FOR(i,1,m)if(Pt[i]>k)
    {
        cnt++;
        update(X[i],Y[i]); 
    }
    FOR(i,1,n)dif[i]+=dif[i-1];
    FOR(i,1,n)if(dif[dfn[i]]==cnt)
        maxcut=max(maxcut,ed[i]);
    return maxer-maxcut<=k;
}
 
int main()
{
    G.clear();
    scanf("%d%d",&n,&m);
    FOR(i,1,n-1)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        G.add(u,v,w);
        G.add(v,u,w);
        maxed=max(maxed,w);
    }
    dfs(1,0,1,0);
    make_path(1,0,1);
    FOR(i,1,m)
    {
        scanf("%d%d",&X[i],&Y[i]);
        lca[i]=LCA(X[i],Y[i]);
        Pt[i]=dis[X[i]]+dis[Y[i]]-2*dis[lca[i]];
        maxer=max(maxer,Pt[i]);
    }
    int l=maxer-maxed,r=maxer,mid;
    while(l<r)
    {
        mid=l+r>>1;
        if(check(mid))
            r=mid;
        else l=mid+1;
    }
    printf("%d\n",l);
    return 0;
}
抽路+前缀最大值
#include<bits/stdc++.h>
#define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i)
#define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i)
typedef long long LL;
using namespace std;
const int N=3e5+3;
template<const int maxn,const int maxm>struct Linked_list
{
    int head[maxn],to[maxm],cost[maxm],nxt[maxm],tot;
    void clear(){memset(head,-1,sizeof(head));tot=0;}
    void add(int u,int v,int w){to[++tot]=v,cost[tot]=w,nxt[tot]=head[u],head[u]=tot;}
    #define EOR(i,G,u) for(int i=G.head[u];~i;i=G.nxt[i])
};
Linked_list<N,N<<1>G;
int dep[N],fa[N],sz[N],son[N],top[N],dis[N];
int X[N],Y[N],Ds[N],n,m,maxer=-1;
int Pt[N],ed[N],ID[N],pref[N],suff[N],p,len,Fa[N];
   
void dfs(int u,int f,int d,int D)
{
    dep[u]=d,fa[u]=f,sz[u]=1,son[u]=0,dis[u]=D;
    EOR(i,G,u)
    {
        int v=G.to[i],w=G.cost[i];
        if(v==f)continue;
        dfs(v,u,d+1,D+w);
        sz[u]+=sz[v];
        if(sz[v]>sz[son[u]])son[u]=v;
    }
}
void make_path(int u,int f,int tp)
{
    top[u]=tp;
    if(son[u])make_path(son[u],u,tp);
    EOR(i,G,u)
    {
        int v=G.to[i];
        if(v==f||v==son[u])continue;
        make_path(v,u,v);
    }
}
int LCA(int x,int y)
{
    while(top[x]!=top[y])
    {
        if(dep[top[x]]>dep[top[y]])x=fa[top[x]];
        else y=fa[top[y]];
    }
    return dep[x]<dep[y]?x:y;
}
void dfs_mark(int u,int f)
{
    EOR(i,G,u)
    {
        int v=G.to[i];
        if(v==f)continue;
        if(Fa[v])continue;
        Fa[v]=Fa[u];
        dfs_mark(v,u);
    }
}
bool Draw(int u,int f,int To)
{
    if(u==To)
    {
        Pt[++len]=u;
        return 1;
    }
    EOR(i,G,u)
    {
        int v=G.to[i],w=G.cost[i];
        if(v==f)continue;
        Pt[++len]=u,ed[len]=w;
        if(Draw(v,u,To))return 1;
        len--;
    }
    return 0;
}
  
  
int main()
{
    G.clear();
    scanf("%d%d",&n,&m);
    FOR(i,1,n-1)
    {
        int u,v,w;
        scanf("%d%d%d",&u,&v,&w);
        G.add(u,v,w);
        G.add(v,u,w);
    }
    dfs(1,0,1,0);
    make_path(1,0,1);
    FOR(i,1,m)
    {
        scanf("%d%d",&X[i],&Y[i]);
        Ds[i]=dis[X[i]]+dis[Y[i]]-2*dis[LCA(X[i],Y[i])];
        if(Ds[i]>maxer)
        {
            maxer=Ds[i];
            p=i;
        }
    }
      
    Draw(X[p],0,Y[p]);
    FOR(i,1,len)
    {
        Fa[Pt[i]]=Pt[i];
        ID[Pt[i]]=i;
    }
    FOR(i,1,len)dfs_mark(Pt[i],0);
      
    int ans=maxer;
    FOR(i,1,m)if(i!=p)
    {
        if(ID[Fa[X[i]]]>ID[Fa[Y[i]]])
            swap(X[i],Y[i]);
        suff[ID[Fa[X[i]]]]=max(suff[ID[Fa[X[i]]]],Ds[i]);
        pref[ID[Fa[Y[i]]]]=max(pref[ID[Fa[Y[i]]]],Ds[i]);
    }
    FOR(i,2,len)pref[i]=max(pref[i],pref[i-1]);
    DOR(i,len-1,1)suff[i]=max(suff[i],suff[i+1]);
      
    FOR(i,1,len-1)ans=min(ans,max(maxer-ed[i],max(pref[i],suff[i+1])));
    printf("%d\n",ans);
    return 0;
}

猜你喜欢

转载自blog.csdn.net/Paulliant/article/details/82864697
今日推荐