[Noip 2013 Day1-3] 货车运输 做法总结
[Noip 2013 Day1-3] 货车运输 做法总结
Online Judge:Luogu-1967
Label:启发式合并,离线,整体二分,按秩合并,倍增,最大生成树
打模拟离线赛时做到,顺便总结一下。
一、启发式合并
离线询问,将询问存在端点上。将每条边按照权值从大到小排列。
依照刚才的顺序依次连上这m条边,利用并查集维护图的连通性。合并时采用启发式合并的思维——将所含元素较小的集合连上较大的集合。对于那个较小的集合,我们直接暴力遍历其中的每个点,再暴力回答那个节点上的询问。
总的时间复杂度为\(O(NlogN)\),由于暴力枚举,常数可能会较大。
#include<bits/stdc++.h>
using namespace std;
const int N=10010;
int n,m,ans[30010],fa[N];
inline int read(){}
struct node{
int u,v,d;
};
vector<node>e;
struct ques{
int to,id;
};
vector<ques>Q[N];
inline bool cmp(node a,node b){
return a.d>b.d;
}
vector<int>son[N];
int find(int x){return x==fa[x]?x:find(fa[x]);}
bool ff;
int main(){
// cout<<(&ff-&f)/1024/1024<<endl;
// freopen("truck.in","r",stdin),freopen("truck.out","w",stdout);
n=read(),m=read();
for(register int i=1;i<=n;++i)fa[i]=i,son[i].push_back(i);
int ma=0;
for(int i=1;i<=m;++i){
int u=read(),v=read(),d=read();
e.push_back((node){u,v,d});
if(d>ma)ma=d;
}
sort(e.begin(),e.end(),cmp);
int q=read();
for(register int i=1;i<=q;++i){
int st=read(),ed=read();
Q[st].push_back((ques){ed,i});
Q[ed].push_back((ques){st,i});
ans[i]=-1;
}
for(int j=0;j<e.size();j++){
int u=e[j].u,v=e[j].v,d=e[j].d;
int A=find(u),B=find(v);
if(A==B)continue;
if(son[A].size()>son[B].size())swap(A,B);
fa[A]=B;
for(int i=0;i<son[A].size();++i){
int x=son[A][i];
for(int j=0;j<Q[x].size();j++){
if(~ans[Q[x][j].id])continue;
if(find(x)==find(Q[x][j].to))ans[Q[x][j].id]=d;
}
son[B].push_back(x);
}
son[A].clear();
}
for(register int i=1;i<=q;++i)printf("%d\n",ans[i]);
}
二、最大生成树+倍增
为了方便表述,令\(u->v\)路径上,所有边权的最小值最大为\(lim(u,v)\),其实就是题目中的询问。
很自然想到重构一棵树,依据刚才的分析,很明显是构造棵最大生成树。最大生成树的写法跟最小生成树的写法一模一样,把边从大到小排序即可。
接下来的询问就是围绕这棵树展开了。
求\(lim(u,v)\)?有点像求树上两点间距离的亚子。联想倍增求LCA的过程,对于\(lim(u,v)\)我们是不是也可以通过倍增维护呢?
定义\(w[i][j]\)表示从\(i\)这个点向上\(2^j\)条边的边权最小值。维护过程跟倍增求LCA维护那个\(fa[i][j]\)数组几乎一样,注意点细节即可。
预处理完\(w\)数组之后,就模拟求LCA的过程,然后顺便算出\(lim(u,v)\)即可。这个做法思路比较顺畅,但实现时注意细节。
#include<bits/stdc++.h>
using namespace std;
const int N=10009;
const int INF=100009;
int bcj[N],fa[N][16],dep[N],vis[N],w[N][16];
int n,m;
struct node{
int u,v,w;
};
vector<node>e;
struct edge{
int to,w;
};
vector<edge>g[N];
inline bool cmp(node a,node b){return a.w>b.w;}
int find(int x){return x==bcj[x]?x:bcj[x]=find(bcj[x]);}
void Buildtree(){
sort(e.begin(),e.end(),cmp);
for(int i=1;i<=n;i++)bcj[i]=i;
for(int i=0;i<e.size();i++){
int u=e[i].u,v=e[i].v,w=e[i].w;
int A=find(u),B=find(v);
if(A==B)continue;
bcj[A]=B;
g[u].push_back((edge){v,w});
g[v].push_back((edge){u,w});
}
}
void dfs(int x,int d){
vis[x]=1,dep[x]=d;
for(int i=0;i<g[x].size();i++){
int y=g[x][i].to;if(vis[y])continue;
fa[y][0]=x;w[y][0]=g[x][i].w;
dfs(y,d+1);
}
}
int LCA(int a,int b){//其实返回的是u到v的路径中,w的最小值
if(find(a)!=find(b))return -1;
if(dep[a]>dep[b])swap(a,b);
int step=dep[b]-dep[a],res=INF;
for(int i=0;i<=15;i++){
if(step&(1<<i))res=min(res,w[b][i]),b=fa[b][i];
}
if(a==b)return res;
for(int i=15;i>=0;i--){
if(fa[a][i]!=fa[b][i]){
res=min(res,w[a][i]);
res=min(res,w[b][i]);
a=fa[a][i],b=fa[b][i];
}
}
res=min(res,w[a][0]);
res=min(res,w[b][0]);
return res;
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int u,v,w;
scanf("%d%d%d",&u,&v,&w);
e.push_back((node){u,v,w});
}
Buildtree();
for(int i=1;i<=n;i++){
if(!vis[i]){
w[i][0]=INF;fa[i][0]=0;
dfs(i,0);
}
}
for(int j=1;j<=15;j++)for(int i=1;i<=n;i++){
fa[i][j]=fa[fa[i][j-1]][j-1];
w[i][j]=min(w[i][j-1],w[fa[i][j-1]][j-1]);
}
int q;scanf("%d",&q);
while(q--){
int u,v;scanf("%d%d",&u,&v);
printf("%d\n",LCA(u,v));
}
}
三、整体二分
ps:整体二分的模板题还有求区间第k大。
对于60%的数据:
我们有一种很low的做法,就是对于每个询问,都去\(O(logZ)\)(Z为图中最大的边权)的二分查找那个两点间边权最小值的最大值——这很明显是二分查找题目的一种套路常见问法。
然后再用\(O(N)\)的时间暴搜一遍check一下。最终时间复杂度为\(O(Q*logZ*N)\)。
优化:
发现询问数很大,上面那样做会TLE。考虑将所有询问一起二分,这样复杂度就变成了\(O(logZ*N)\),但是常数可能会因为一些奇奇怪怪的操作而变得有点大。
看代码会比较好理解233:
#include<bits/stdc++.h>
using namespace std;
const int N=10009,INF=100000;
int n,m,qnum,res[30010];
struct edge{int u,v,w;}e[50010];
inline bool edgecmp(edge a,edge b){return a.w>b.w;}
struct node{
int u,v;
int l,r,mid,id,ans;
}q[30010];
inline bool querycmp(node a,node b){return a.mid>b.mid;}
int fa[N];
int find(int x){return x==fa[x]?x:fa[x]=find(fa[x]);}
void init(){for(int i=1;i<=n;i++)fa[i]=i;}
void merge(int a,int b){
int A=find(a),B=find(b);
if(A!=B)fa[A]=B;
}
void Global_Search(){
sort(e+1,e+m+1,edgecmp);
int times=20;//log2(INF)
while(times--){
int cur=1;
sort(q+1,q+qnum+1,querycmp);
init();
for(int i=1;i<=qnum;i++){
if(q[i].l>q[i].r)continue;
while(cur<=m&&e[cur].w>=q[i].mid){
merge(e[cur].u,e[cur].v);cur++;
}
int qu=q[i].u,qv=q[i].v;
int A=find(qu),B=find(qv);
if(A==B){q[i].l=q[i].mid+1,q[i].ans=q[i].mid;}
else q[i].r=q[i].mid-1;
q[i].mid=(q[i].l+q[i].r)>>1;
}
}
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;i++){
int u,v,w;scanf("%d%d%d",&u,&v,&w);
e[i]=((edge){u,v,w});
}
scanf("%d",&qnum);
for(int i=1;i<=qnum;i++){
scanf("%d%d",&q[i].u,&q[i].v);
q[i].l=0,q[i].r=INF,q[i].mid=(0+INF)>>1;
q[i].id=i;q[i].ans=-1;
}
Global_Search();
for(int i=1;i<=qnum;i++)res[q[i].id]=q[i].ans;
for(int i=1;i<=qnum;i++)printf("%d\n",res[i]);
}
此题还可以按秩合并,做法与前面几种类似,就不赘述勒。。
[Noip 2013 Day1-3] 货车运输 做法总结的更多相关文章
- NOIP 2013 day1
tags: 模拟 快速幂 逆序对 树状数组 归并排序 最小生成树 lca 倍增 categories: 信息学竞赛 总结 tex live 2017.iso 转圈游戏 火柴排队 货车运输 转圈游戏 s ...
- JZOJ 3534. 【NOIP2013提高组day1】货车运输
Description A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在有 q 辆货车在运输货物,司机们想知道每辆车在不超过车辆限重的 ...
- noip不知道哪年 货车运输
题意:最大生成树上找 q组两个点的lca 然后求出u->lca->v这条路径上的最小边 倍增大法好 # include <iostream> # include <std ...
- NOIP 2013 货车运输【Kruskal + 树链剖分 + 线段树 】【倍增】
NOIP 2013 货车运输[树链剖分] 树链剖分 题目描述 Description A 国有 n 座城市,编号从 1 到 n,城市之间有 m 条双向道路.每一条道路对车辆都有重量限制,简称限重.现在 ...
- Codevs 3287 货车运输 2013年NOIP全国联赛提高组(带权LCA+并查集+最大生成树)
3287 货车运输 2013年NOIP全国联赛提高组 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 传送门 题目描述 Description A 国有 n 座 ...
- noip 2013 提高组 day1
1.转圈游戏: 解析部分略,快速幂就可以过 Code: #include<iostream> #include<fstream> using namespace std; if ...
- NOIP2013 货车运输 (最大生成树+树上倍增LCA)
死磕一道题,中间发现倍增还是掌握的不熟 ,而且深刻理解:SB错误毁一生,憋了近2个小时才调对,不过还好一遍AC省了更多的事,不然我一定会疯掉的... 3287 货车运输 2013年NOIP全国联赛提高 ...
- C++之路进阶——codevs3287(货车运输)
3287 货车运输 2013年NOIP全国联赛提高组 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题目描述 Description A 国有 n ...
- Codevs3278[NOIP2013]货车运输
3287 货车运输 2013年NOIP全国联赛提高组 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题目描述 Description A 国有 ...
随机推荐
- Java--会移动、反弹的球
package firstpack; import java.awt.*; public class MyStar { public static void main(String[] args) { ...
- 5、 postman的鉴权
什么是鉴权? 鉴权(authentication)是指验证用户是否拥有访问系统的权利.常用的有两种鉴权方式,一种是session鉴权,一种是jwt鉴权,相对而言,后者居多. 实例: 比如有一个添加角色 ...
- MongoDB点滴
0 http://blog.csdn.net/mydeman/article/details/6652387 1 MongoDB 内置连接池,不需要使用额外的连接池驱动 Note: The Mongo ...
- centos 根目录扩容
添加一块磁盘 参考上一篇博文VMware Workstation 添加磁盘 挂载目录(centos) 查看当前磁盘挂载情况 [root@node1 ~]# fdisk -l Disk /dev/sda ...
- tcp_tw_recycle和tcp_timestamps的一些知识(转)
现在很多公司都用LVS做负载均衡,通常是前面一台LVS,后面多台后端服务器,这其实就是NAT,当请求到达LVS后,它修改地址数据后便转发给后端服务器,但不会修改时间戳数据,对于后端服务器来说,请求的源 ...
- Soci介绍
soci是一个用C++封装的数据库访问库,目前通过 “前端(应用程序)/核心/后端(数据库适配)”模式支持firebird,mysql,sqlite3,oracle,postgresql,odbc多种 ...
- php 获取不到post的值
一般在ajax提交的时候出现这种情况 当我们在ajax参数设置 contentType: 'application/json; charset=utf-8', 用file_get_contents ...
- input输入内容成可点击状态
<!DOCTYPE html> <html> <head> <script src="//code.jquery.com/jquery-1.9.1. ...
- TCP/IP协议,,OSI的七层参考模型,HTTP请求响应机制
一.TCP/IP协议 TCP/IP是Transmission Control Protocol/Internet Protocol的简写,中译名为传输控制协议/因特网互联协议,又名网络通讯协议,是In ...
- Java 几种队列区别的简单说明
前言 队列,字面意思就可以明白. 是一种线性的数据暂存与管理工具. 也可以让各种业务功能进行逐个的队列运行. 此篇博客只说明一下Java有几种队列 未阻塞和阻塞队列的区别 未阻塞: 1.未阻塞的队列在 ...