NOIp2013 货车运输 By cellur925

题目传送门

A 国有 n 座城市,编号从 1 到 n ,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。

现在有 q 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。

 

思路

这题思路想明白了就很简单,一句话题意:求树上两点间路线中边长最小的边权。

首先,为什么是树呢?限重肯定越大越好,因此我们可以跑出图的最大生成树(Kruskal)

之后,我们就可以对于每次询问来求出路径上的最小边权(最多能运的货物重量),这里可以用到树上倍增法(求LCA时一起求了,虽然LCA不会直接用到,但也是要一起求出来的)

至于两点间是否能互达,就可以用并查集维护一下就行了!

 

实现


提交记录过于真实,从0到10到65到70到95到AC Orz

可能是我常数写丑了,所以在洛谷更新评测姬前以及不开氧气优化都会T两个点??

code

  1 #include<cstdio>
  2 #include<algorithm>
  3 #include<queue>
  4 #include<cmath>
  5 #include<cstring>
  6 #define maxm 50090
  7 #define maxn 10090
  8 
  9 using namespace std;
 10 
 11 int n,m,Q,tot,t;
 12 int head[maxn],d[maxn],f[maxn][31],fa[maxn],g[maxn][31];
 13 bool vis[maxn];
 14 struct cellur{
 15     int f,t,w;
 16 }e[maxm];
 17 struct node{
 18     int to,next,val;
 19 }edge[maxn<<1];
 20 
 21 bool cmp(cellur a,cellur b)
 22 {
 23     return a.w>b.w;
 24 } 
 25 
 26 void add(int x,int y,int z)
 27 {
 28     edge[++tot].to=y;
 29     edge[tot].next=head[x];
 30     head[x]=tot;
 31     edge[tot].val=z;
 32 }
 33 
 34 int getf(int x)
 35 {
 36     if(fa[x]==x) return x;
 37     else return fa[x]=getf(fa[x]);
 38 }
 39 
 40 void Kruskal()
 41 {
 42     for(int i=1;i<=n;i++) fa[i]=i;
 43     sort(e+1,e+1+m,cmp);
 44     int cnt=0;
 45     for(int i=1;i<=m;i++)
 46     {
 47         if(cnt==n-1) break;
 48         int pp=getf(e[i].f);
 49         int qq=getf(e[i].t);
 50         if(pp==qq) continue;
 51         cnt++;
 52         fa[qq]=pp;
 53         add(e[i].f,e[i].t,e[i].w),add(e[i].t,e[i].f,e[i].w);
 54     }
 55 }
 56 
 57 void bfs(int s)
 58 {
 59     queue<int>q;
 60     q.push(s);d[s]=1;
 61     while(!q.empty())
 62     {
 63         int u=q.front();q.pop();
 64         for(int i=head[u];i;i=edge[i].next)
 65         {
 66             int v=edge[i].to;
 67             if(d[v]) continue;
 68             d[v]=d[u]+1;
 69             f[v][0]=u;g[v][0]=edge[i].val;
 70             for(int j=1;j<=t;j++)
 71             {
 72                 f[v][j]=f[f[v][j-1]][j-1];
 73                 g[v][j]=min(g[f[v][j-1]][j-1],g[v][j-1]);
 74             }
 75             q.push(v);
 76         }
 77     }
 78 }
 79 
 80 int lca(int x,int y)
 81 {
 82     int num=1e9;
 83     if(d[x]<d[y]) swap(x,y);
 84     for(int i=t;i>=0;i--)
 85         if(d[f[x][i]]>=d[y]) num=min(num,g[x][i]),x=f[x][i];
 86     if(x==y) return num;
 87     for(int i=t;i>=0;i--)
 88         if(f[x][i]!=f[y][i])
 89         {
 90             num=min(num,g[x][i]);
 91             num=min(num,g[y][i]);
 92             x=f[x][i],y=f[y][i];
 93         } 
 94     return min(num,min(g[x][0],g[y][0]));
 95 }
 96 
 97 int main()
 98 {
 99     freopen("truck.in","r",stdin);
100     freopen("truck.out","w",stdout);
101     scanf("%d%d",&n,&m);
102     t=log2(n)+1;
103     for(int i=1;i<=m;i++)
104         scanf("%d%d%d",&e[i].f,&e[i].t,&e[i].w);
105     Kruskal();
106     memset(g,63,sizeof(g));
107     for(int i=1;i<=n;i++) if(!d[i]) bfs(i);
108 //    for(int i=1;i<=n;i++) printf("%d ",g[i][2]);
109 //    return 0;
110     scanf("%d",&Q);
111     while(Q--)
112     {
113         int x=0,y=0;
114         scanf("%d%d",&x,&y);
115         int pp=getf(x);
116         int qq=getf(y);
117         if(pp!=qq) {printf("-1\n");continue;}    
118         printf("%d\n",lca(x,y));
119     }
120     return 0;
121 }
View Code

$bfs$也能解决不连通的问题:不bfs一次,而是让每个点都有深度。

11.6考试的时候边大小开小了&&LCA写错板子了hhh

转载于:https://www.cnblogs.com/nopartyfoucaodong/p/9504852.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
信息数据从传统到当代,是一直在变革当中,突如其来的互联网让传统的信息管理看到了革命性的曙光,因为传统信息管理从时效性,还是安全性,还是可操作性等各个方面来讲,遇到了互联网时代才发现能补上自古以来的短板,有效的提升管理的效率和业务水平。传统的管理模式,时间越久管理的内容越多,也需要更多的人来对数据进行整理,并且数据的汇总查询方面效率也是极其的低下,并且数据安全方面永远不会保证安全性能。结合数据内容管理的种种缺点,在互联网时代都可以得到有效的补充。结合先进的互联网技术,开发符合需求的软件,让数据内容管理不管是从录入的及时性,查看的及时性还是汇总分析的及时性,都能让正确率达到最高,管理更加的科学和便捷。本次开发的医院后台管理系统实现了病房管理、病例管理、处方管理、字典管理、公告信息管理、患者管理、药品管理、医生管理、预约医生管理、住院管理、管理员管理等功能。系统用到了关系型数据库中王者MySql作为系统的数据库,有效的对数据进行安全的存储,有效的备份,对数据可靠性方面得到了保证。并且程序也具备程序需求的所有功能,使得操作性还是安全性都大大提高,让医院后台管理系统更能从理念走到现实,确确实实的让人们提升信息处理效率。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值