NOIP2015复赛提高组之运输计划

来源:互联网 发布:传智播客c语言教程 编辑:程序博客网 时间:2024/06/05 16:59

noip2015运输计划

题目(二分猜答案,LCA,差分)

题目描述

公元 2044 年,人类进入了宇宙纪元。L 国有 n 个星球,还有 n−1 条双向航道,每条航道建立在两个星球之间,这 n−1 条航道连通了 L 国的所有星球。小 P 掌管一家物流公司, 该公司有很多个运输计划,每个运输计划形如:有一艘物流飞船需要从 ui 号星球沿最快的宇航路径飞行到 vi 号星球去。显然,飞船驶过一条航道是需要时间的,对于航道 j,任意飞船驶过它所花费的时间为 tj,并且任意两艘飞船之间不会产生任何干扰。为了鼓励科技创新, L 国国王同意小 P 的物流公司参与 L 国的航道建设,即允许小P 把某一条航道改造成虫洞,飞船驶过虫洞不消耗时间。在虫洞的建设完成前小 P 的物流公司就预接了 m 个运输计划。在虫洞建设完成后,这 m 个运输计划会同时开始,所有飞船一起出发。当这 m 个运输计划都完成时,小 P 的物流公司的阶段性工作就完成了。如果小 P 可以自由选择将哪一条航道改造成虫洞, 试求出小 P 的物流公司完成阶段性工作所需要的最短时间是多少?

输入

第一行包括两个正整数 n,m,表示 L 国中星球的数量及小 P 公司预接的运输计划的数量,星球从 1 到 n 编号。接下来 n−1 行描述航道的建设情况,其中第 i 行包含三个整数 ai,bi 和 ti,表示第 i 条双向航道修建在 ai 与 bi 两个星球之间,任意飞船驶过它所花费的时间为 ti。数据保证 1≤ai,bi≤n 且 0≤ti≤1000。接下来 m 行描述运输计划的情况,其中第 j 行包含两个正整数 uj 和 vj,表示第 j 个运输计划是从 uj 号星球飞往 vj号星球。数据保证 1≤ui,vi≤n

输出

输出文件只包含一个整数,表示小 P 的物流公司完成阶段性工作所需要的最短时间。

样例输入

6 3
1 2 3
1 6 4
3 1 7
4 3 6
3 5 5
3 6
2 5
4 5

样例输出

11

数据范围:n,m<=300000

分析

首先可以很明确这是棵树,数据范围咋int内,时间复杂度可以达到nlog2(n),这个时间复杂度就应该可以用二分猜答案,仔细去思考,发现这种删除问题恰好适合。
关于判断函数,思路就是记录下所有超过答案的链,求其交集,将其交集中最大的一条边删了,再看是否满足,其中有些小技巧见代码,另外交集怎么求呢?
先说一个技巧:判断是否所有的都走过只需要记录下一共走的次数,判断是否等于走的人数,暴力算法就是分别把所有的路径都标出来,但是转念一想,这么标记就太粗暴了,时间复杂度耗在标记上不亏嘛?正好就是用了差分的思想,正好最近在学数列,理解起来也算是相对容易

/*难度的确比较高,但是只要分成几个部分就很容易关键词:二分猜答案,LCA,差分*/#include<cstdio>#include<cstring>#include<queue>#include<cmath>#include<algorithm>#include<iostream>#include<vector>using namespace std;#define maxn 300005#define FOR for(int j=18;j>=0;j--)void Rd(int &x){//这是从一个大神那里偷来的     x=0;char p;    while(p=getchar(),p<'0');    do{        x=(x<<1)+(x<<3)+(p^48);    }while(p=getchar(),p>='0');}struct edge{    int to,next,w;}E[maxn<<1];struct data{    int u,v,z,dist;//我觉得这样应该可以减少计算     friend bool operator<(data a,data b)    {        return a.dist>b.dist;    }}p[maxn];int n,m,np,first[maxn],dist[maxn],dep[maxn],fa[maxn][20],b[maxn],a[maxn];void add(int u,int v,int w){    E[++np]=(edge){v,first[u],w};    first[u]=np;}void Debug(){    for(int i=1;i<=m;i++)        cout<<p[i].u<<" "<<p[i].v<<" "<<p[i].dist<<endl;    cout<<endl;}void Init(){    Rd(n);    Rd(m);    int u2,u1,w;    for(int i=1;i<n;i++)    {        Rd(u1);Rd(u2);Rd(w);        add(u1,u2,w);        add(u2,u1,w);    }    for(int i=1;i<=m;i++)        scanf("%d%d",&p[i].u,&p[i].v);}//以上都是常规准备 void DFS0(int i,int f){    a[i]=b[i];    for(int p=first[i];p;p=E[p].next)    {        int j=E[p].to;        if(j==f)continue;        DFS0(j,i);        a[i]+=a[j];    }}bool calc(int x)//若距离可以为x则返回1{    memset(b,0,sizeof(b));    int Max=p[1].dist-x,k;    data v;    v.dist=x;    k=lower_bound(p+1,p+m+1,v)-p;    k--;    for(int i=1;i<=m;i++)    {        if(p[i].dist-x>0)        {            b[p[i].u]++;            b[p[i].v]++;            b[p[i].z]-=2;        }//这里的标记我本来是从两头开始直接向父亲标记,时间复杂度明显太高,这是的连串标记恰好是差分数组的特征         else break;//排序的好处     }    int mx=0;//这里是0而不能是-1因为如果没有找到的话不造成影响     DFS0(1,0    );    for(int i=1;i<=n;i++)        if(a[i]==k)//这里的技巧是直接计算走过次数,如果达到最大值,就可以判断为交集             mx=max(mx,dist[i]-dist[fa[i][0]]);    if(mx>=Max)return 1;    return 0;}void DFS(int i,int f,int d,int t){    dep[i]=d;    dist[i]=t;    fa[i][0]=f;    for(int j=1;j<=18;j++)        fa[i][j]=fa[fa[i][j-1]][j-1];    for(int p=first[i];p;p=E[p].next)    {        int j=E[p].to,c=E[p].w;        if(j==f)continue;        DFS(j,i,d+1,t+c);    }}int LCA(int u,int v)//这道题需要最近公共祖先 {    if(dep[u]<dep[v])swap(u,v);    int x=dep[u]-dep[v];    FOR        if(x&(1<<j))u=fa[u][j];    if(u==v)return u;    FOR        if(fa[u][j]!=fa[v][j])            u=fa[u][j],v=fa[v][j];    return fa[u][0];}void ready(){    DFS(1,0,1,0);       for(int i=1;i<=m;i++)    {        p[i].z=LCA(p[i].u,p[i].v);        p[i].dist=dist[p[i].u]+dist[p[i].v]-2*dist[p[i].z];    }    sort(p+1,p+n+1);//排序是一种很常见的化简,这里的确好了许多 }void solve()//这种算法可以判断可行,时间复杂度log2(n)刚刚好的,二分猜答案就很妙 {    int A=0,B=1000*maxn,ans,mid; //这里已经是最小范围了,自己加了个深度*1000的优化就错了,建议二分猜答案不用实时数据,而只是用恒定值     while(A<=B)    {        mid=(A+B)/2;        if(calc(mid))        {            ans=mid;            B=mid-1;        }        else             A=mid+1;    }    cout<<ans;}int main(){    freopen("in.txt","r",stdin);    Init();    ready();//  Debug();    //时刻检查当然是个好习惯    solve();    return 0;}

心得

思路一定要先弄清楚,函数用一个函数写一个,用伪代码的形式,最后恰好从下往上读,不然的话程序会搞得很乱。
关于树的题目,请一定要把样例树画出来

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 华为微信运动步数为零怎么办 淘宝店铺没货了客户拍了怎么办 房子涨价了卖家反悔不卖了怎么办 买的东西很贵质量不好怎么办 在淘宝开的店账号忘了怎么办 建了个淘宝优惠券群没人购物怎么办 刚开的淘宝店没有生意怎么办 房产代理公司不给渠道结佣金怎么办 天猫超过72小时不发货怎么办 流量魔盒苹果下载怎么打不开怎么办 淘宝包邮店铺新疆地区拍怎么办 淘宝客服当顾客要优惠时怎么办 微信手机号注册的找不到了怎么办 之前注册的微信找不到了怎么办 苹果ipad的id密码忘了怎么办 淘宝和支付宝用一张银行卡怎么办 淘宝卖家填写虚假物流信息怎么办 淘宝店铺的浏览量越来越少怎么办 网上充手机话费充错了怎么办 夜神模拟器上陌陌的位置不对怎么办 如果在大庭广众之下放了个屁怎么办 淘宝分销上传宝贝被系统下架怎么办 酷狗喜欢歌单里面的歌都没了怎么办 苹果手机下载不了微信缓冲怎么办 登陆微信提示版本过低登不了怎么办 苹果手机微信版本过低登不上怎么办 微信小程序显示微信版本过低怎么办 三星手机登微信显示版本过低怎么办 微信版本低无法登录无法升级怎么办 手机淘宝五应用界面无法打开怎么办 入住淘宝主播没有微博粉丝怎么办 手机淘宝领金币怎么没有了怎么办 淘宝荬家缺货对付款买家怎么办 淘宝买家确认收货后申请退款怎么办 淘宝东西失效了但付过款了怎么办 淘宝图片被投诉盗图怎么办原图没了 淘宝退款申请不小心撤销了怎么办 淘宝不小心点了撤销退款怎么办 淘宝上退款不小心撤销了怎么办 新店淘宝卖家想开通直播怎么办 想开通淘宝直播却没有粉丝怎么办