NOIP2013 货车运输 [LCA] [RMQ] [最小生成树] [ST倍增]
来源:互联网 发布:电气原理图软件 编辑:程序博客网 时间:2024/04/28 05:50
题目描述 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。
首先看到这题, 由于要最大, 肯定是求最大生成树
那么 o(n2) dfs 求任意点对之间的最小边是可以想到的
但是看看数据范围肯定TLE
于是暴力链剖, 不过要注意query的时候判断的时候要 m+-1 但是递归下去要用m , 可以画图举特例分析
1AC 代码:
#include<iostream>#include<cstdio>#include<cstring>#include<cstdlib>#include<cmath>#include<vector>#include<queue>#include<stack>#include<map>#include<set>#include<string>#include<iomanip>#include<ctime>#include<cctype>#include<algorithm>#ifdef WIN32#define AUTO "%I64d"#else#define AUTO "%lld"#endifusing namespace std;#define smin(x,tmp) x=min((x),(tmp))const int INF=0x3f3f3f3f;const int maxn=10005;const int maxm=50005;int fa[maxn];int find(int x) { return fa[x]==x?x:fa[x]=find(fa[x]); }inline bool union_find(int x,int y){ int t1=find(x),t2=find(y); if(t1==t2) return false; fa[t2]=t1; return true;}map <pair<int,int>,int> g;struct Edge{ int to,next; int val;}edge[maxm<<1];int head[maxn];int maxedge;inline void addedge(int u,int v,int c){ edge[++maxedge]=(Edge){v,head[u],c}; head[u]=maxedge; edge[++maxedge]=(Edge){u,head[v],c}; head[v]=maxedge; g[make_pair(u,v)]=c; g[make_pair(v,u)]=c;}struct Road{ int from,to; int cost; bool operator < (const Road t) const { return cost>t.cost;// querying the Biggest MST !!!! }}road[maxm];int n,m;int f[maxn],son[maxn],size[maxn],depth[maxn];int top[maxn],id[maxn],rid[maxn];int maxnode;//for segment tree (one-demensional array)int dfs1(int u,int father,int deep){ f[u]=father,size[u]=1,depth[u]=deep; for(int i=head[u];~i;i=edge[i].next) { int v=edge[i].to; if(v==father) continue; size[u]+=dfs1(v,u,deep+1); if(!son[u]||size[son[u]]<size[v]) son[u]=v; } return size[u];}void dfs2(int u,int tp){ top[u]=tp;id[u]=++maxnode;rid[maxnode]=u;// non de mixer la id-rid !! if(son[u]) dfs2(son[u],tp); for(int i=head[u];~i;i=edge[i].next) { int v=edge[i].to; if(v==f[u]||v==son[u]) continue; dfs2(v,v); }}int tree[maxn<<2];//minvoid build(int root,int l,int r){ if(r-l==1) { tree[root]=g[make_pair(rid[l],rid[r])]; return; } int m=(l+r)>>1; build(root<<1,l,m); build(root<<1|1,m,r); tree[root]=min(tree[root<<1],tree[root<<1|1]);}int query(int root,int l,int r,int x,int y)//query min{ if(x==y) return INF;//must!! when query the same node !!!!!! if(x<=l&&r<=y) return tree[root]; int m=(l+r)>>1; int t1=INF,t2=INF; if(x<=m-1&&l<=y) t1=query(root<<1,l,m,x,y);//here too, use x<=m-1 in case stucking at m if(y>=m+1&&r>=x) t2=query(root<<1|1,m,r,x,y);//be conscious of m or m+1, query m but judge m+1 return min(t1,t2);}int Find(int u,int v)//find min{ int t1=top[u],t2=top[v]; int ret=INF; while(t1^t2) { if(depth[t1]<depth[t2]) swap(t1,t2),swap(u,v); smin(ret,query(1,1,maxnode,id[t1],id[u])); smin(ret,g[make_pair(t1,f[t1])]); u=f[t1]; t1=top[u]; } if(depth[u]<depth[v]) swap(u,v); return min(ret,query(1,1,maxnode,id[v],id[u]));}inline void init(){ scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) scanf("%d%d%d",&road[i].from,&road[i].to,&road[i].cost); for(int i=1;i<=n;i++) fa[i]=i; memset(head,-1,sizeof(head)); maxedge=-1;}void kruskal(){ sort(road+1,road+m+1); int pos=1,tot=0; while(pos<=m && tot^(n-1)) { if(union_find(road[pos].from,road[pos].to)) tot++,addedge(road[pos].from,road[pos].to,road[pos].cost); pos++; }}bool vis[maxn];//for union_findvoid build_forest(){ for(int i=1;i<=n;i++) { int father=find(i); if(!vis[father]) { vis[father]=true; dfs1(father,0,1); dfs2(father,father); } } build(1,1,maxnode);}int main(){ freopen("truck.in","r",stdin); freopen("truck.out","w",stdout); init(); kruskal(); build_forest(); int q; scanf("%d",&q); while(q--) { int x,y; scanf("%d%d",&x,&y); if(find(x)^find(y)) printf("-1\n"); else printf("%d\n",Find(x,y)); } return 0;}
但是NOIP正解一定不是链剖, 此题要用到 LCA 的 ST 倍增算法, 并且属于精确的查询,没有重叠部分, 可以用sum等进行替换, 边dfs边更新
用 f[i][j] = f[f[i][j-1]][j-1] 保存 i 前面的第2^i 节点, 与普通 RMQ 不同
用 dp[i][j] = min ( dp[i][j-1] , dp[f[i][j-1]][j-1] ) 来维护倍增的最小值 ( 当然 sum 也一样 , 因为精确求范围, 满足区间加 )
AC代码:
#include<iostream>#include<cstdio>#include<cstring>#include<cstdlib>#include<cmath>#include<vector>#include<queue>#include<stack>#include<map>#include<set>#include<string>#include<iomanip>#include<ctime>#include<cctype>#include<algorithm>#ifdef WIN32#define AUTO "%I64d"#else#define AUTO "%lld"#endifusing namespace std;#define smin(x,tmp) x=min((x),(tmp))#define smax(x,tmp) x=max((x),(tmp))const int INF=0x3f3f3f3f;const int maxn=10005;const int maxm=50005;const int maxd=20;int fa[maxn];int find(int x) { return fa[x]==x?x:fa[x]=find(fa[x]); }inline bool union_find(int x,int y){ int t1=find(x),t2=find(y); if(t1==t2) return false; fa[t2]=t1; return true;}struct Edge{ int to,next; int val;}edge[maxm<<1];int head[maxn];int maxedge;inline void addedge(int u,int v,int c){ edge[++maxedge]=(Edge){v,head[u],c}; head[u]=maxedge; edge[++maxedge]=(Edge){u,head[v],c}; head[v]=maxedge;}struct Road{ int from,to; int cost; bool operator < (const Road t) const { return cost>t.cost; }}road[maxm];int n,m;inline void init(){ scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) scanf("%d%d%d",&road[i].from,&road[i].to,&road[i].cost); for(int i=1;i<=n;i++) fa[i]=i; memset(head,-1,sizeof(head)); maxedge=-1;}void kruskal(){ sort(road+1,road+m+1); int pos=1,tot=0; while(pos<=m && tot^(n-1)) { if(union_find(road[pos].from,road[pos].to)) tot++,addedge(road[pos].from,road[pos].to,road[pos].cost); pos++; }}int f[maxn][maxd+5],dp[maxn][maxd+5]; // the node 2^j after u int depth[maxn];void dfs(int u,int deep){ depth[u]=deep; for(int k=1;(1<<k)<=n;k++) { f[u][k] = f[f[u][k-1]][k-1]; dp[u][k] = min(dp[u][k-1] , dp[f[u][k-1]][k-1]); } for(int i=head[u];~i;i=edge[i].next) { int v=edge[i].to; if(!depth[v]) { f[v][0]=u;dp[v][0]=edge[i].val;//initialize here rather than in the former context!! no considering the roor coz INF is INF, not changing and not visiting the value!! dfs(v,deep+1); } }}int Find(int u,int v){ int ans=INF; if(depth[u] < depth[v]) swap(u,v);// making the u is deeper!! // make u v at the same depth for(int k=maxd;k>=0;k--) // k>=0 here!! or cannot jump at the same depth!!! if(depth[v] <= depth[f[u][k]]) // f[0] = 0, indicates its beyond the root!! { smin( ans , dp[u][k] ); u=f[u][k]; } if(u == v) return ans; //special judge of one of them is the LCA // jump at the same time for(int k=maxd;k>=0;k--) // k>=0 here too!! if(f[u][k] ^ f[v][k]) { smin(ans , min( dp[u][k] , dp[v][k] ) ); u = f[u][k]; v = f[v][k]; } return min( ans , min(dp[u][0] , dp[v][0])); // u v this time is the F1 of LCA}int main(){ freopen("truck.in","r",stdin); freopen("truck.out","w",stdout); init(); kruskal(); memset(dp,0x3f,sizeof(dp)); for(int i=1;i<=n;i++) if(!depth[i]) dfs(i,1); int q; scanf("%d",&q); while(q--) { int x,y; scanf("%d%d",&x,&y); if(find(x)^find(y)) printf("-1\n"); else printf("%d\n",Find(x,y)); } return 0;}
顺便写了一个利用dfn求LCA的方法 HDU 2874
#include<iostream>#include<cstdio>#include<cstring>#include<cstdlib>#include<cmath>#include<vector>#include<queue>#include<stack>#include<map>#include<set>#include<string>#include<iomanip>#include<ctime>#include<climits>#include<cctype>#include<algorithm>#ifdef WIN32#define AUTO "%I64d"#else#define AUTO "%lld"#endifusing namespace std;const int maxn=10005;struct Edge{ int to,next; int val;}edge[maxn*maxn];int head[maxn];int maxedge;inline void addedge(int u,int v,int c){ edge[++maxedge]=(Edge){v,head[u],c}; head[u]=maxedge; edge[++maxedge]=(Edge){u,head[v],c}; head[v]=maxedge;}int n,m,q;int fa[maxn];int find(int x) { return fa[x]==x?x:fa[x]=find(fa[x]); }bool union_find(int x,int y){ int t1=find(x),t2=find(y); if(t1==t2) return false; fa[t2]=t1; return true;}int maxnode;int dfn[maxn],ver[maxn<<1];//dfn: first visit maxnode, ver: reverse function of dfn, indicating the number of vertexint depth[maxn<<1],dis[maxn];//depth: the depth of dfn, dis: from root to vertexinline bool init(){ if(!~scanf("%d%d%d",&n,&m,&q)) return false; for(int i=1;i<=n;i++) fa[i]=i; memset(head,-1,sizeof(head)); memset(dfn,0,sizeof(dfn)); maxedge=-1;maxnode=0; for(int i=1;i<=m;i++) { int u,v,c; scanf("%d%d%d",&u,&v,&c); addedge(u,v,c); union_find(u,v); } return true;}void dfs(int u,int deep){ dfn[u]=++maxnode;ver[maxnode]=u;depth[maxnode]=deep; for(int i=head[u];~i;i=edge[i].next) { int v=edge[i].to; if(dfn[v]) continue; dis[v]=dis[u]+edge[i].val; dfs(v,deep+1); depth[++maxnode]=deep;ver[maxnode]=u; }}const int maxdepth=20;int dp[maxn<<1][maxdepth];void ST(int n)// n=::maxnode{ for(int i=1;i<=n;i++) dp[i][0]=i; for(int j=1;(1<<j)<=n;j++)//careful of the limits for(int i=1;i+(1<<j)-1<=n;i++)//careful of the limits by y=dp[i+(1<<j-1)][j-1] and the limit of i+(1<<j)-1<=n, must -1 'coz the i+(1<<j)-1 is possible to be n!! { int x=dp[i][j-1],y=dp[i+(1<<j-1)][j-1]; dp[i][j]=depth[x]<depth[y]?x:y; }}inline int RMQ(int l,int r){ int k=0; while(1<<(k+1)<=r-l+1) k++;// careful of the limits!! int x=dp[l][k],y=dp[r-(1<<k)+1][k]; return depth[x]<depth[y]?x:y;}inline int LCA(int u,int v){ int x=dfn[u],y=dfn[v]; if(x>y) swap(x,y); int root=RMQ(x,y); return ver[root];}int main(){ freopen("city.in","r",stdin); freopen("city.out","w",stdout); while(init()) { for(int i=1;i<=n;i++) if(!dfn[i]) dfs(i,1); ST(maxnode); while(q--) { int x,y; scanf("%d%d",&x,&y); if(find(x)^find(y)) printf("Not connected\n"); else printf("%d\n",dis[x]+dis[y]-(dis[LCA(x,y)]<<1)); } } return 0;}
- NOIP2013 货车运输 [LCA] [RMQ] [最小生成树] [ST倍增]
- [NOIP2013]货车运输 D1 T3 kruscal最大生成树+树上倍增lca+rmq
- 【noip2013提高组day1T3】 货车运输 RMQ+LCA+倍增+最大生成树
- NOIP2013 货车运输 (最大生成树+树上倍增LCA)
- 倍增LCA NOIP2013 货车运输
- 【洛谷】1967 [noip2013]货车运输 最小生成树+LCA
- bzoj3732(同货车运输,倍增lca+最小生成树)
- NOIP2013货车运输(最大生成树+LCA)
- NOIP2013 货车运输(最大生成树+LCA)
- [NOIP2013]货车运输,最大生成树+LCA
- [NOIP2013] 货车运输 最大生成树 LCA
- NOIP2013货车运输 最大生成树+lca
- 【NOIP2013提高组T3】货车运输-最大生成树+倍增LCA
- NOIP2013 货车运输 解题报告(最大生成树+倍增lca)
- 洛谷 1967 [NOIP2013] 货车运输 最大生成树+倍增
- NOIP2013货车运输 (最大生成树,分块求LCA)
- noip2013货车运输 最大生成树+LCA 【pascal】
- [noip2013] 货车运输(最大生成树+并查集+LCA)
- SpringBoot学习笔记(2) Spring Boot的一些配置
- AlertDialog
- 转:java获取系统常量
- adoquery.FilterGroup参数解释如下
- mxGraph API
- NOIP2013 货车运输 [LCA] [RMQ] [最小生成树] [ST倍增]
- osx + win7 双系统安装
- BZOJ 1624: [Usaco2008 Open] Clear And Present Danger 寻宝之路
- 骨牌覆盖去除切割线的情况(DP+容斥原理)
- UVALive 7270 Osu! Master 水题
- File类的使用1
- javaweb
- 搜索树判断
- 三人行之C从零开始