(暴力55分+卡常95分补全版)NOIP 2015 D2 T3运输计划

9 篇文章 0 订阅
5 篇文章 0 订阅

【问题描述】 公元2044年,人类进入了宇宙纪元。 L国有n个星球,还有n-1条双向航道,每条航道建立在两个星球之间,这n-1
条航道连通了L国的所有星球。
小P掌管一家物流公司,该公司有很多个运输计划,每个运输计划形如:有一艘物流飞船需要从ui号星球沿最快的宇航路径飞行到vi号星球去。显然,
飞船驶过一条航道是需要时间的,对于航道j,任意飞船驶过它所花费的时间为tj,并且任意两艘飞船之间不会产生任何干扰。
为了鼓励科技创新,L国国王同意小P的物流公司参与L国的航道建设,即允许小P把某一条航道改造成虫洞,飞船驶过虫洞不消耗时间。
在虫洞的建设完成前小P的物流公司就预接了m个运输计划。在虫洞建设完成后,这m个运输计划会同时开始,所有飞船一起出发。当这m个运输计划都完成时,小P
的物流公司的阶段性工作就完成了。 如果小P可以自由选择将哪一条航道改造成虫洞,试求出小P
的物流公司完成阶段性工作所需要的最短时间是多少?
【输入格式】 输入文件名为 transport.in。 第一行包括两个正整数n、m,表示L国中星球的数量及小P公司预接的运输计划的数量,星球从1到n编号。 接下来n-1行描述航道的建设情况,其中第i行包含三个整数ai, bi和ti,表示第i条双向航道修建在ai与bi两个星球之间,任意飞船驶过它所花费的时间为ti。 接下来m行描述运输计划的情况,其中第j行包含两个正整数uj和vj,表示第j个运输计划是从uj号星球飞往vj号星球。
【输出格式】 输出文件名为transport.out。 共1行,包含1个整数,表示小P的物流公司完成阶段性工作所需要的最短时间。
【数据范围】
对于 20%的数据,m=1。
对于另外 40%的数据,第 i 号小区与第 i+1 号小区相连。
对于以上 60%的数据,保证 0 <= n, m <= 100000
对于 100%的数据,0 <= n, m <= 200000, wi <= 1000,答案保证在
10^9 范围内

先立个flag,在搞图论的期间一定要刚掉这个题!

前天队内胡策,某二组出了运输计划…考场上打了半小时的暴力刚出来了55分。

对于一档数据直接暴力LCA去掉权值最大的一条边即可。
上暴力的代码

#include<algorithm>
#include<cstring>
#include<cstdio>
#include<string>
#include<cstdlib>
using namespace std;
const int maxn=200000+500;
struct Edge
{
    int f;
    int to;
    int d;
    int next;
}edge[maxn];
int tot,head[maxn];
void add(int f,int t,int d)
{
    edge[++tot].f=f;
    edge[tot].d=d;
    edge[tot].to=t;
    edge[tot].next=head[f];
    head[f]=tot;
}
int n,m;
int dist[maxn],deep[maxn],fa[maxn];
void dfs(int f,int t)
{
    fa[t]=f;
    deep[t]=deep[f]+1;
    for(int i=head[t];i;i=edge[i].next)
    {
        Edge e=edge[i];
        if(!deep[e.to])
        {
            dist[e.to]=e.d;
            dfs(t,e.to);
        }
    }
}
int lca(int x,int y)
{
    int tot=0;
    int maxdist=0;
    if(deep[x]<deep[y])
        swap(x,y);
    while(deep[x]!=deep[y])
    {
        tot+=dist[x];
        maxdist=max(maxdist,dist[x]);
        x=fa[x];
    }
    while(x!=y)
    {
        tot+=dist[x];
        maxdist=max(maxdist,dist[x]);
        x=fa[x];
        tot+=dist[y];
        maxdist=max(maxdist,dist[y]);
        y=fa[y];

    }
    return tot-maxdist;
}
void solve1()
{
    int f,t;
    scanf("%d%d",&f,&t);
    int ans=lca(f,t);
    printf("%d\n",ans);
}
void read(int &a)
{
    char c=getchar();
    while(c<'0'||c>'9')
        c=getchar();
    int ans=0;
    while(c>='0'&&c<='9')
    {
        ans*=10;
        ans+=c-'0';
        c=getchar();
    }
    a=ans;
    return;
}
int cnt[maxn];
int qzh[maxn];
int dis[maxn];
int totd[maxn];
int ll[maxn];
int rr[maxn];
void solve2()
{
    int maxx=0;
    int ans1;
    int poi;
    for(int i=1;i<n;i++)
    {
        qzh[i]+=qzh[i-1]+dis[i];
    }

    for(int i=1;i<=m;i++)
    {
        int f,t;
        read(f),read(t);
        ll[i]=min(f,t);
        rr[i]=max(f,t)-1;
        totd[i]=qzh[max(f,t)-1]-qzh[min(f,t)-1];
        if(totd[i]>maxx)
        {

            poi=i;
            maxx=totd[i];
            ans1=maxx;
        }
    }

    for(int i=ll[poi];i<=rr[poi];i++)
    {
        int ans=0;
        for(int j=1;j<=m;j++)
        {
            if(rr[j]<i||ll[j]>i)
                continue;
            ans=max(totd[j]-dis[i],ans);
        }
        maxx=min(ans,maxx);
    }
    printf("%d\n",maxx);
}

int main()
{
    read(n),read(m);
    for(int i=1;i<n;i++)
    {
        int a,b,c;
        read(a),read(b),read(c);
        add(a,b,c);
        add(b,a,c);
        dis[min(a,b)]=c;
    }
    dfs(100005,1);
    if(m==1)
    {
        solve1();
    }
    else if(n<=100000&&m<=100000)
    {
        solve2();
    }
    else
    {
        puts("-1");
    }
    return 0;
}

嗯,下面就是其中一个95分思路。
这个题中:
当这m个运输计划都完成时,小P的物流公司的阶段性工作就完成了
小P的物流公司完成阶段性工作所需要的最短时间。
所以说这个题就是在符合某种条件的情况下求最大值最小。
二分!
直接二分答案。
那么,对于一个mid值,我们该怎么验证呢?
预处理的时候记录一下每一条运输计划的起点,终点,两者的LCA,总长度。
然后以长度为关键字从大到小排序。
我们从1—>m for一遍,直到找到一条运输计划的长度小于mid,记录当前位置poi,如果当前运输计划的长度大于mid,我们就要考虑它所在的路径,怎么维护呢?用树上前缀和,起点和终点++,lca-=2就可以。

处理出来求一遍前缀和,得到的是在这前poi-1条运输计划中,每条边被经过的次数。
我们找到那些被经过poi-1次的边,然后用当前最大的运输计划来减这条边,如果减去之后小于等于mid,直接return false(我这里设的是return false的时候右边界减小)。

正确性呢?
如果我们选出一条不是被这前poi个运输计划经过的边删掉,则不能保证这前poi条运输计划的总长都减小了,而如果减去的边都不在这前poi个运输计划内,则不能保证答案更优….

这个题最后一个点卡常卡的我心累…..
加了各种常数优化,今天早上还学了DQS学长的DFS序求前缀和….最后一个点还是死活TLE掉了。

#include<algorithm>
#include<cstdlib>
#include<cstring>
#include<cstdio>
#include<queue>
#define mem(a) memset(a,0,sizeof(a))
using namespace std;
const int maxn=3e5+500;
int n,m;
struct Edge
{
    int to;
    int d;
    int next;
}edge[maxn<<1];
int head[maxn];
int tot;
inline void add(int f,int t,int d)
{
    edge[++tot]=(Edge){t,d,head[f]};
    head[f]=tot;
}
int ii;
int dist[maxn],deep[maxn],fa[maxn][30];
inline void dfs1(int f,int t,int dep)
{
    deep[t]=dep;
    fa[t][0]=f;
    for(register int j=1;j<=ii;++j)
        fa[t][j]=fa[fa[t][j-1]][j-1];
    for(register int i=head[t];i;i=edge[i].next)
    {
        Edge e=edge[i];
        if(e.to==f||deep[e.to])
            continue;
        dist[e.to]=dist[t]+e.d;
        dfs1(t,e.to,dep+1);
    }   
}
int lca(int x,int y)
{
    if(deep[x]<deep[y])
        swap(x,y);
    for(register int j=ii;j>=0;--j)
        if(deep[fa[x][j]]>=deep[y])
            x=fa[x][j];
    if(x==y)
        return x;
    for(register int j=ii;j>=0;--j)
        if(fa[x][j]!=fa[y][j])
            x=fa[x][j],y=fa[y][j];
    return fa[x][0];
}
int bq[maxn],qzh[maxn];
struct meico
{
    int f;
    int t;
    int d;
    int lca;
}sz[maxn];
int ask_(int x,int y)
{
    return deep[x]>deep[y]?x:y;
}
int s[maxn];
int top;
bool can(int mid)
{
    for(register int i;i<=n;++i)
        qzh[i]=0;
    int poi=m+1;
    int tot=0;
    for(register int i=1;i<=m;++i)
    {
        meico hah=sz[i];
        if(hah.d<=mid)
        {
            poi=i;
            break;
        }
        else
        {
            qzh[hah.f]++,qzh[hah.t]++;
            qzh[hah.lca]-=2;
            tot++;
        }
    }
    if(!tot)
        return true;
    for(int i=top;i>=0;i--)
    {
        qzh[fa[s[i]][0]]+=qzh[s[i]];
    }
    int maxe=0;
    for(register int i=1;i<=n;++i)
    {
        meico hah=sz[i];
        if(qzh[i]==poi-1)
        {
            if(sz[1].d-bq[i]<=mid)
                return false;
            maxe=max(maxe,bq[i]);   
        }
    }
    return true;
}
bool cmp(meico a,meico b)
{
    return a.d>b.d;
}
int divv(int r)
{
    sort(sz+1,sz+1+m,cmp);
    int ans=r;
    int l=0;
    while(l<=r)
    {
        int mid=(l+r)>>1;
        if(can(mid))
            l=mid+1;
        else
            r=mid-1,ans=min(ans,mid);
    }
    return ans;
}
inline void read(int &a)
{
    char c=getchar();
    while(c<'0'||c>'9')
        c=getchar();
    int ans=0;
    while(c<='9'&&c>='0')
        ans*=10,ans+=c-'0',c=getchar();
    a=ans;
    return;
}
queue<int>q;

inline void bfs()
{
    q.push(1);
    while(!q.empty())
    {
        int x=q.front();
        q.pop(); 
        s[++top]=x;
        for(int i=head[x];i;i=edge[i].next)
        {
            Edge e=edge[i];
            if(e.to!=fa[x][0])
                q.push(e.to);
        }
    }
}
int ff[maxn],tt[maxn],dd[maxn];
int main()
{
    freopen("transport.in","r",stdin);  
    freopen("transport.out","w",stdout);

    read(n),read(m);
    for(ii=1;(1<<ii)<=n;++ii);
    for(register int i=1;i<n;++i)
    {
        read(ff[i]),read(tt[i]),read(dd[i]);
        add(ff[i],tt[i],dd[i]);
        add(tt[i],ff[i],dd[i]);
    }
    dfs1(1,1,1);
    int maxx=0;
    for(register int i=1;i<=m;++i)
    {
        read(sz[i].f),read(sz[i].t);
        sz[i].lca=lca(sz[i].f,sz[i].t);
        sz[i].d=dist[sz[i].f]+dist[sz[i].t]-(dist[sz[i].lca]<<1);
        maxx=max(maxx,sz[i].d);
    }
    for(register int i=1;i<n;++i)
        bq[ask_(ff[i],tt[i])]=dd[i];
    bfs();  
    printf("%d",divv(maxx));
    fclose(stdin);
    fclose(stdout);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值