NOIP2013货车运输
题目描述 Description
A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路。每一条道路对车辆都有重量限制,简称限重。现在有 q 辆货车在运输货物,司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。
输入描述 Input Description
第一行有两个用一个空格隔开的整数 n,m,表示 A 国有 n 座城市和 m 条道路。
接下来 m 行每行 3 个整数 x、y、z,每两个整数之间用一个空格隔开,表示从 x 号城市到 y 号城市有一条限重为 z 的道路。注意:x 不等于 y,两座城市之间可能有多条道路。
接下来一行有一个整数 q,表示有 q 辆货车需要运货。
接下来 q 行,每行两个整数 x、y,之间用一个空格隔开,表示一辆货车需要从 x 城市运输货物到 y 城市,注意:x 不等于 y。
输出描述 Output Description
输出共有 q 行,每行一个整数,表示对于每一辆货车,它的最大载重是多少。如果货车不能到达目的地,输出-1。
样例输入 Sample Input
4 3
1 2 4
2 3 3
3 1 1
3
1 3
1 4
1 3
样例输出 Sample Output
3
-1
3
数据范围及提示 Data Size & Hint
对于 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。
题解
因为不在最大生成森林上的路径一定是更劣的
那么如果我们只拿出最大生成森林做spfa的话,边的大小降为n,可以通过60%的数据
其实本题是模板题,询问森林中两个点的路径上的最小边权,连通性用并查集判一下
可以用树上倍增在logn的复杂度解决一棵树内的一次询问
预处理F(i,j)表示第i个点距离为2^j的祖先,这个可以深搜整棵树再递推一下,复杂度nlogn
G(i,j)表示第i个点到其距离为2^j的祖先上的最小权值,然后用倍增的思想俩个点往上跳一跳更新答案就行辣
30分 spfa
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
#include<iostream> #include<cstdio> #include<cstring> #define INF 0x7fffffff using namespace std; int n,m,q,ne; int head[10001]; struct data{ int to,next,v; }e[100001]; void insert(int u,int v,int w) { ne++; e[ne].to=v; e[ne].v=w; e[ne].next=head[u]; head[u]=ne; } void work(int x,int y) { int q[10001],dis[10001],t=0,w=1; int now,i; bool inq[10001]; memset(inq,0,sizeof(inq)); memset(dis,-1,sizeof(dis)); q[0]=x;inq[x]=1; dis[x]=INF; while(t!=w) { now=q[t];t++;if(t==n)t=0; i=head[now]; while(i) { if(dis[e[i].to]<min(dis[now],e[i].v)) { dis[e[i].to]=min(dis[now],e[i].v); if(!inq[e[i].to]) { inq[e[i].to]=1; q[w]=e[i].to; w++;if(w==n)w=0; } } i=e[i].next; } inq[now]=0; } printf("%d\n",dis[y]); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { int x,y,z; scanf("%d%d%d",&x,&y,&z); insert(x,y,z);insert(y,x,z); } scanf("%d",&q); for(int i=1;i<=q;i++) { int x,y; scanf("%d%d",&x,&y); work(x,y); } return 0; } |
60分 最大生成树+spfa
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define INF 0x7fffffff using namespace std; int n,m,q,ne; int head[10001],father[10001]; struct data1{ int x,y,v; }ed[50001]; struct data2{ int to,next,v; }e[100001]; int find(int x){return x==father[x]? father[x]:father[x]=find(father[x]);} inline bool cp(data1 a,data1 b){return a.v>b.v;} void insert(int u,int v,int w) { ne++; e[ne].to=v; e[ne].v=w; e[ne].next=head[u]; head[u]=ne; } void work(int x,int y) { int q[10001],dis[10001],t=0,w=1; int now,i; bool inq[10001]; memset(inq,0,sizeof(inq)); memset(dis,-1,sizeof(dis)); q[0]=x;inq[x]=1; dis[x]=INF; while(t!=w) { now=q[t];t++;if(t==n)t=0; i=head[now]; while(i) { if(dis[e[i].to]<min(dis[now],e[i].v)) { dis[e[i].to]=min(dis[now],e[i].v); if(!inq[e[i].to]) { inq[e[i].to]=1; q[w]=e[i].to; w++;if(w==n)w=0; } } i=e[i].next; } inq[now]=0; } printf("%d\n",dis[y]); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) {scanf("%d%d%d",&ed[i].x,&ed[i].y,&ed[i].v);} sort(ed+1,ed+m+1,cp); for(int i=1;i<=n;i++)father[i]=i; for(int i=1;i<=m;i++) { int x=ed[i].x,y=ed[i].y,v=ed[i].v; if(find(x)!=find(y)) { father[find(x)]=find(y); insert(x,y,v);insert(y,x,v); } } scanf("%d",&q); for(int i=1;i<=q;i++) { int x,y; scanf("%d%d",&x,&y); work(x,y); } return 0; } |
2014.4.9:TAT我以前写最大生成树还写spfa干嘛。。。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
#include<iostream> #include<algorithm> #include<cstdio> #include<cstring> #define inf 0x7fffffff using namespace std; int n,m,q,cnt,tot,deep[10001],head[10001],f[10001],fa[10001][17],d[10001][17]; bool vis[10001]; struct edge{int x,y,v;}a[50001]; struct e{int next,to,v;}e[20001]; void ins(int u,int v,int w) {e[++cnt].to=v;e[cnt].next=head[u];head[u]=cnt;e[cnt].v=w;} void insert(int u,int v,int w) {ins(u,v,w);ins(v,u,w);} int find(int x) {return f[x]==x?x:f[x]=find(f[x]);} bool cmp(edge a,edge b) {return a.v>b.v;} void dfs(int x) { vis[x]=1; for(int i=1;i<=16;i++) { if(deep[x]<(1<<i))break; fa[x][i]=fa[fa[x][i-1]][i-1]; d[x][i]=min(d[x][i-1],d[fa[x][i-1]][i-1]); } for(int i=head[x];i;i=e[i].next) { if(vis[e[i].to])continue; fa[e[i].to][0]=x; d[e[i].to][0]=e[i].v; deep[e[i].to]=deep[x]+1; dfs(e[i].to); } } int lca(int x,int y) { if(deep[x]<deep[y])swap(x,y); int t=deep[x]-deep[y]; for(int i=0;i<=16;i++) if((1<<i)&t)x=fa[x][i]; for(int i=16;i>=0;i--) { if(fa[x][i]!=fa[y][i]) {x=fa[x][i];y=fa[y][i];} } if(x==y)return x; return fa[x][0]; } int ask(int x,int f) { int mn=inf; int t=deep[x]-deep[f]; for(int i=0;i<=16;i++) { if(t&(1<<i)) { mn=min(mn,d[x][i]); x=fa[x][i]; } } return mn; } int main() { memset(d,127/3,sizeof(d)); scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)f[i]=i; for(int i=1;i<=m;i++) scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].v); sort(a+1,a+m+1,cmp); for(int i=1;i<=m;i++) { int x=a[i].x,y=a[i].y; int p=find(a[i].x),q=find(a[i].y); if(p!=q) { f[p]=q; insert(x,y,a[i].v); tot++;if(tot==n-1)break; } } for(int i=1;i<=n;i++)if(!vis[i])dfs(i); scanf("%d",&q); for(int i=1;i<=q;i++) { int x,y;scanf("%d%d",&x,&y); if(find(x)!=find(y)){printf("-1\n");continue;} else { int t=lca(x,y); printf("%d\n",min(ask(x,t),ask(y,t))); } } return 0; } |
不用倍增。。
网站做的好棒哦
求问黄学长您的&是什么意思。。。
位运算QAQ
QAQ具体是什么意思
(1<<i)&x 表示 x二进制的第i位是否为1?
倍增算法QAQ
代码插件怎么弄的?求教!谢谢
他叫你黄学长
你一定认识DXY
啊。。。我知道但是不认识人
应该说树上倍增是求LCA的一种方法吧QAQ,强烈推荐去学tarjan的离线LCA求法
唉没写过太懒了
貌似3712只能用tarjan?
膜拜