【题解】NOIP-2013 货车运输

题目描述

A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。

输入输出格式

输入格式:

输入文件名为 truck.in。

输入文件第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道

路。 接下来 m 行每行 3 个整数 x、 y、 z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z 的道路。注意: x 不等于 y,两座城市之间可能有多条道路 。

接下来一行有一个整数 q,表示有 q 辆货车需要运货。

接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,注意: x 不等于 y 。

输出格式:

输出文件名为 truck.out。

输出共有 q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货

车不能到达目的地,输出-1。

输入输出样例

输入样例#1:

4 3
1 2 4
2 3 3
3 1 1
3
1 3
1 4
1 3

输出样例#1:

3
-1
3

说明

对于 30%的数据,0 < n < 1,000,0 < m < 10,000,0 < q< 1,000;

对于 60%的数据,0 < n < 1,000,0 < m < 50,000,0 < q< 1,000;

对于 100%的数据,0 < n < 10,000,0 < m < 50,000,0 < q< 30,000,0 ≤ z ≤ 100,000。

题意

给定一张无向图,每条边上有权值,有q组询问,每次询问从Si到Ei的所有路径中,最小边权最大为多少,若不连通则为-1

思路

首先,发现数据有一点大,50000条边,30000组询问,好像O(n)或O(m)的复杂度都过不了,那么,肯定有些边是不需要遍历的,由于本蒟蒻图论学得不好,不会对dijkstra进行基因改造,只好用生成树的野生算法

小插曲

16日晚上,校长从办公楼步行前往教学楼,中途来到信息组。校长进入一机房。看到boshi和一群学生十分激动。校长走到讲台上拍了拍他的肩膀,问我们是哪个年级的,boshi看了我们后说是17届新高一的。信息组当晚开了两个机房,校长走过去,饶有兴趣地了解有关情况。

校长亲临机房,提出了具有重要战略意义的讲话,并提出,在当前NOIP临近的紧要关头,机房的思想文化建设不能丢,不仅现在不能丢,将来也不能丢,这事关机房的可持续发展,机房的几项原则也不能丢。

这是我们组马克思主义信息化的又一次历史性进步和全组理论水平的又一次历史性提高。这次历史性飞跃、历史性进步和历史性提高的意义和作用,同样不能低估。这是我们组理论上发展的根本标志。它攸关一中信息组、一中信息化建设和一中信息社会的前途命运。

在校长的系列讲话下,我们机房的士气高涨。是啊,如果这道题都不能A,那还怎么在校长的带领下为校争光呢?
———文章摘自《机房时报》

继续

于是这题我们就可以采用删边的思想简化复杂度,由于博主十分菜,一看到题就会想贪心骗分,所以这题咱就用贪心吧(竟然是正解),用一点脑筋感性地想一想,就会发现所有点对间的最大限制路径一定会在最大限制生成树上不会证的自己百度

那么,我们就用kruscarl求出图中的最大生成树,然后发现,题目已经被简化到O(nq)了,但是还不够,再想一想校长是如何勉励我们乘长风破万里浪,攻克难关的,心中一股心酸,马上想到,可以用LCA啊,可以快速地求出Si到Ei的路径,但是仍不够,想一想NOIP的数据,就会知道,时间复杂度绝对又会被卡到O(nq)

那还有优化吗?答案是有,想一想树链剖分实际上我也不知道是什么,我们可以在LCA时顺带求最小值啊,只要想维护倍增数组时再维护一个minn[][]就可以一起求值了,时间复杂度O(qlogn)
比如说倍增的数组(本人喜欢用anc)伪递推公式:

anc[i][j]=anc[anc[i][j1]][j1] a n c [ i ] [ j ] = a n c [ a n c [ i ] [ j − 1 ] ] [ j − 1 ]

那么我们就仿造它写一个与anc绑定的minn数组:
minn[i][j]=min(minn[i][j1],minn[anc[i][j1]][j1]) m i n n [ i ] [ j ] = m i n ( m i n n [ i ] [ j − 1 ] , m i n n [ a n c [ i ] [ j − 1 ] ] [ j − 1 ] )

不要忘了初始化:
minn[i][0]=idad[i] m i n n [ i ] [ 0 ] = i 与 d a d [ i ] 之 间 的 边 权

至此,此题AC

说一句,数组一定要开大点

做这题前,看见有人说这题要注意数组范围,我就笑笑,后来发现自己也入坑了

反正数组开大点又不差那点

再说一句,c++中的log是以10为底数的

做题时想设一个maxlog=log2(n)卡卡时间,后来发现……唉……

左改右改终于改对了的代码:

#include<bits/stdc++.h>
using namespace std;
#define merge(x,y) dad[father(x)]=father(y)
#define check(x,y) (father(x)==father(y))
#define cl(x) memset(x,0,sizeof(x))
#define inf 2000000001

template <typename _Tp> inline void read(_Tp &x){
    char c11=getchar();x=0;bool booo=0;
    while(c11<'0'||c11>'9'){if(c11=='-')booo=1;c11=getchar();}
    while(c11>='0'&&c11<='9'){x=x*10+c11-'0';c11=getchar();}
    if(booo)x=-x;
    return ;
}

const int maxn=10050,maxm=50050;
struct so{int l,r,w;bool bo;}a[maxm];
long long dad[maxn],mi[maxn][20],n,m,h;
struct node{long long next,v,w;}b[maxm<<2];long long p=0,head[maxn];
long long depth[maxn],anc[maxn][20],maxlog;
long long str[maxn],stt=0;

inline long long father(long long x){if(!dad[x])return x;return dad[x]=father(dad[x]);}
inline bool my_comp(const so aa,const so bb){return aa.w>bb.w;}
void init_k();
void work_k();
void ex();
void add(long long,long long,long long);
void dfs(long long,long long,long long,long long);

long long lca(long long x,long long y){
    if(!(father(x)==father(y)))return -1;
    long long ans=inf;
    if(depth[x]<depth[y])swap(x,y);

    if(depth[x]!=depth[y])
        for(long long i=maxlog;i>-1;i--)
            if(depth[y]<=depth[anc[x][i]]){
                ans=min(ans,mi[x][i]);
                x=anc[x][i];
            }
    if(x==y) return ans;

    for(long long i=maxlog;i>-1;i--)
        if(anc[x][i]!=anc[y][i]){
            ans=min(ans,mi[x][i]);x=anc[x][i];
            ans=min(ans,mi[y][i]);y=anc[y][i];
        }
    return min(min(ans,mi[x][0]),mi[y][0]);
}

void query(){
    maxlog=(log(n)/log(2))+3;
    long long T;
    read(T);
    long long A,B;
    for(long long i=1;i<=T;i++){
        read(A);read(B);
        printf("%lld\n",lca(A,B));
    }
    return ;
}

/***************************************************/
int main(){

    cl(dad);cl(head);cl(depth);

    init_k();

    work_k();

    ex();

    for(long long i=1;i<=stt;i++)
        dfs(str[i],0,1,inf);

    query();

    return 0;
}
/***************************************************/
void dfs(long long x,long long last,long long deep,long long zhi){
    depth[x]=deep;
    anc[x][0]=last;
    mi[x][0]=zhi;
    for(long long i=1;i<=15;i++){
        anc[x][i]=anc[anc[x][i-1]][i-1];
        mi[x][i]=min(zhi,min(mi[x][i-1],mi[anc[x][i-1]][i-1]));
    }
    for(long long i=head[x];i;i=b[i].next)
        if(b[i].v!=last){
            long long v=b[i].v;
            dfs(v,x,deep+1,b[i].w);
        }
}

void add(long long u,long long v,long long w){
    b[++p].v=v;
    b[p].w=w;
    b[p].next=head[u];
    head[u]=p;
}

void ex(){
    for(long long i=1;i<=m;i++)
        if(a[i].bo){
            add(a[i].l,a[i].r,a[i].w);
            add(a[i].r,a[i].l,a[i].w);
        }

    for(long long i=0;i<=n;i++)
    for(long long j=0;j<=15;j++)
        mi[i][j]=inf;
    return ;
}

void work_k(){
    set <long long> csdn;
    sort(a+1,a+m+1,my_comp);
    h=0;
    for(long long i=1;i<=m;i++){
        if(!check(a[i].l,a[i].r)){
            merge(a[i].l,a[i].r);
            a[i].bo=1;
            h++;
        }
        if(h==n-1)
            break;
    }
    for(long long i=1;i<=n;i++){
        long long fa=father(i);
        if(!csdn.count(fa)){
            csdn.insert(fa);
            str[++stt]=i;
        }
    }
    return ;
}

void init_k(){
    read(n);read(m);
    for(long long i=1;i<=m;i++){
        read(a[i].l);read(a[i].r);
        read(a[i].w);a[i].bo=0;
    }
    return ;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值