运输计划

萝らか妹 提交于 2020-02-07 15:49:35

传送门

解法:

首先要学会求 树的最近公共祖先(LCA)

没用树剖
用了一个经常可以代替树剖的方法
树上差分 这个方法很优秀 一定要掌握

首先 有了lca 就可以求树上两点间长度
\(d[x]\)\(x\) 到 根节点 的距离
\(u\),\(v\) 间距离为 \(d[u]+d[v]-2*d[lca(u,v)]\)

同时可以实现差分
\(ver[x]\)\(x\) 的差分数组
对于 \(u\),\(v\) 连边 覆盖原图边的差分处理 \(ver[u]++,ver[v]++,ver[lca(u,v)]-=2\)
在进行一次dfs就求到 每个点x到父节点的边 被覆盖的次数ver[x]

二分答案

对于每个\(u\),\(v\)若距离>二分的值
说明此时要在其覆盖的边中减去一条满足条件的
差分处理

设此时有cnt个路径要删边
那么能达到此时二分答案则必须有一条边被所有路径覆盖
即dfs后 存在\(ver[x]==cnt\)且此时删去改边后 最长的一条路径也要不大于此时答案
此答案才可行

最后二分求出的最大可行值即为答案

代码:

第一种 倍增法求lca O(nlogn+mlogm+(n+m)logSum) 可能会被卡掉一两个点 开O2能过

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<map>
#define inf 2000000000
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define dwn(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
typedef long long ll;
int n,m,sum=0,ans;
int root=1,dep[300010],f[300010][20],lg2[300010],d[300010],ver[300010];
int tot=1,head[300010];
struct node
{
    int u,v,lca,dis;
}qu[300010];
struct EDGE
{
    int to,nxt,d;
}edge[600010];
queue<int> que;
inline void add(int u,int v,int d)
{
    edge[++tot].to=v;
    edge[tot].nxt=head[u];
    edge[tot].d=d;
    head[u]=tot;
}
inline void bfs()
{
    dep[root]=1;
    que.push(root);
    while(!que.empty())
    {
        int x=que.front();que.pop();
        for(int i=head[x];i;i=edge[i].nxt)
        {
            int y=edge[i].to;
            if(dep[y]) continue;
            f[y][0]=x;dep[y]=dep[x]+1;d[y]=d[x]+edge[i].d;
            rep(j,1,lg2[dep[y]])
            {
                f[y][j]=f[f[y][j-1]][j-1];
            }
            que.push(y);
        }
    }
}
inline int lca(int x,int y)
{
    if(dep[x]>dep[y]) swap(x,y);
    dwn(i,lg2[dep[y]-dep[x]],0)
        if(dep[f[y][i]]>=dep[x]) y=f[y][i];
    if(x==y) return x;
    dwn(i,lg2[dep[x]],0)
        if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    return f[x][0];
}
void dfs(int x)
{
    for(int i=head[x];i;i=edge[i].nxt)
    {
        int y=edge[i].to;
        if(dep[x]>dep[y]) continue;
        dfs(y);
        ver[x]+=ver[y];
    }
}
bool check(int mid)
{
    memset(ver,0,sizeof(ver));
    int cnt=0,maxn=0;
    rep(i,1,m)
    {
        if(qu[i].dis>mid)
        {
            cnt++;
            ver[qu[i].u]++,ver[qu[i].v]++,ver[qu[i].lca]-=2;
            maxn=max(maxn,qu[i].dis-mid);
        }
    }
    if(cnt==0) return 1;
    dfs(root);
    rep(i,1,n)
        if(i!=root&&ver[i]==cnt&&d[i]-d[f[i][0]]>=maxn) return 1;
    return 0;
}
int main()
{
    scanf("%d%d",&n,&m);
    rep(i,2,n)
    {
        lg2[i]=lg2[i>>1]+1;
    }
    rep(i,1,n-1)
    {
        int u,v,d;
        scanf("%d%d%d",&u,&v,&d);
        add(u,v,d),add(v,u,d);
        sum+=d;
    }
    bfs();
    rep(i,1,m)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        qu[i].lca=lca(u,v);
        qu[i].dis=d[u]+d[v]-2*d[qu[i].lca];
        qu[i].u=u,qu[i].v=v;
    }
    int l=0,r=sum;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(check(mid)) r=mid-1,ans=mid;
        else l=mid+1;
    }
    printf("%d\n",ans);
    return 0;
}

第二种 tarjan的lca算法 O(n+m+(n+m)logSum) 相对快一些 不用O2过

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
#include<queue>
#include<map>
#define inf 2000000000
#define min(x,y) ((x)<(y)?(x):(y))
#define max(x,y) ((x)>(y)?(x):(y))
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define dwn(i,a,b) for(int i=(a);i>=(b);--i)
using namespace std;
typedef long long ll;
typedef pair<int,int> P;
int n,m,sum=0,ans;
int root=1,d[300010],fa[300010],ver[300010];
int tot=1,head[300010];
int v[300010],par[300010];
struct node
{
    int u,v,lca,dis;
}qu[300010];
struct EDGE
{
    int to,nxt,d;
}edge[600010];
queue<int> que;
vector<P> ques[300010];
inline void add(int u,int v,int d)
{
    edge[++tot].to=v;
    edge[tot].nxt=head[u];
    edge[tot].d=d;
    head[u]=tot;
}
inline void bfs()
{
    que.push(root);
    fa[root]=root;
    while(!que.empty())
    {
        int x=que.front();que.pop();
        for(int i=head[x];i;i=edge[i].nxt)
        {
            int y=edge[i].to;
            if(fa[y]) continue;
            d[y]=d[x]+edge[i].d;
            fa[y]=x;
            que.push(y);
        }
    }
}
int get(int x)
{
    if(x==par[x]) return x;
    return par[x]=get(par[x]);
}
void tarjan(int x)
{
    v[x]=1;
    for(int i=head[x];i;i=edge[i].nxt)
    {
        int y=edge[i].to;
        if(v[y]) continue;
        tarjan(y);
        par[y]=x;
    }
    for(int i=0;i<ques[x].size();++i)
    {
        if(v[ques[x][i].first]==2)
            qu[ques[x][i].second].lca=get(ques[x][i].first);
    }
    v[x]=2;
}
void dfs(int x)
{
    for(int i=head[x];i;i=edge[i].nxt)
    {
        int y=edge[i].to;
        if(fa[y]!=x) continue;
        dfs(y);
        ver[x]+=ver[y];
    }
}
bool check(int mid)
{
    memset(ver,0,sizeof(ver));
    int cnt=0,maxn=0;
    rep(i,1,m)
    {
        if(qu[i].dis>mid)
        {
            cnt++;
            ver[qu[i].u]++,ver[qu[i].v]++,ver[qu[i].lca]-=2;
            maxn=max(maxn,qu[i].dis-mid);
        }
    }
    if(cnt==0) return 1;
    dfs(root);
    rep(i,1,n)
        if(i!=root&&ver[i]==cnt&&d[i]-d[fa[i]]>=maxn) return 1;
    return 0;
}
int main()
{
    scanf("%d%d",&n,&m);
    rep(i,1,n-1)
    {
        int u,v,d;
        scanf("%d%d%d",&u,&v,&d);
        add(u,v,d),add(v,u,d);
        sum+=d;
    }
    bfs();
    rep(i,0,n) par[i]=i;
    rep(i,1,m)
    {
        int u,v;
        scanf("%d%d",&u,&v);
        qu[i].u=u,qu[i].v=v;
        if(u==v) qu[i].lca=u,qu[i].dis=0;
        else ques[u].push_back(P(v,i)),ques[v].push_back(P(u,i));
    }
    tarjan(root);
    rep(i,1,m) qu[i].dis=d[qu[i].u]+d[qu[i].v]-2*d[qu[i].lca];
    int l=0,r=sum;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(check(mid)) r=mid-1,ans=mid;
        else l=mid+1;
    }
    printf("%d\n",ans);
    return 0;
}
标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!