【题解】P4886快递员
【题解】P4886 快递员
淀粉质好题!!!加深了我对点分治的理解。最近分治学了好多啊。
题目大意
给定你一颗有边权的树,再给你\(m\)和点对,请你在树上选出来一个点,使得所有点对到这个点的距离的最大值最小。请你输出最小的最长距离。
数据范围
对于\(100\%\)的数据,\(n,m\le10^5,w_i \le 10^3\)
思路
刚开始看到这个题目,以为题干在暗示我二分答案。于是设计一个这样的算法
先二分,得到一个\(log10^8=26\)的系数,得到一个答案\(k\)后,每次先枚举\(O(m)\)的点,然后在树剖的线段树上把这\(2m\)个点扩展\(k\)距离,最后查询是否有点被遍历了\(2m\)次,返回\(true \ or \ false\) 实现二分。复杂度大约\(=6\times10^8\),无奈过不去。
先分析题目的一个重要性质!假设我们已经选中了一个点,并且发现产生最大距离的点对当中,有一条路径经过了那个点,那么此时一定是最优答案!
怎么得到的?你看我随便移开到不是这个端点的地方,最大答案只会变大啊!
那么我们可以设计\(O(nm)\)的算法,直接枚举我们选择的快递中心\(v\),然后\(O(n)\)得到所有点到此点的距离,和若删去此点后在的子树的编号,直接\(O(m)\)枚举所有的点对,查询\(dis_{i_1}+dis_{i_2}\)得到距离最大值,查询是否在同一颗子树里得到改点在它们的路径上。但这样显然过不去。
\(Solution\)
考虑优化,假若我发现对于最大值产生贡献的点对都在我的一颗子树内,那么我就往这颗子树里走,这样才有一点可能更新更优解。这样就可以把其他子树给剪枝了不是?而且继续考虑产生最优解时的性质,如果存在多组对最大值产生贡献的点对在不同的子树内,此时我同样一定是最优解。把问题缩小变成的量\(=\)我跳的那个子树的大小
联系淀粉质,考虑怎么样可以把剪枝最大化,直接跳树的重心啊,这样每次要么确定答案,要么把问题化为至多\(n'=0.5n\)的大小,这样就是\(O(nlogn)\)了。
讲一讲写点分治会犯的错误吧。主要是我不熟练,也很傻。
共产风
意思就是不清空上次统计答案造成的影响
- 没有清零当前根的\(dis\)
- 不回溯自己的树状数组,线段树等
浮夸风
打字前没有\(orz \ yyb\)
瞎指挥
意思就是跳随机点了!
和随机分治有异曲同工之妙
产生随机跳的情况包括但不限于:
- 不初始化\(rt\)
- 找错\(rt\)
- 遍历了\(usd\)的点
- 不调用\(rt\)
- 不重置\(sum\)
上好看的代码
#include<bits/stdc++.h>
using namespace std;typedef long long ll;
#define DRP(t,a,b) for(register int t=(a),edd=(b);t>=edd;--t)
#define RP(t,a,b) for(register int t=(a),edd=(b);t<=edd;++t)
#define ERP(t,a) for(register int t=head[a];t;t=e[t].nx)
#define midd register int mid=(l+r)>>1
#define TMP template < class ccf >
TMP inline ccf qr(ccf b){
register char c=getchar();register int q=1;register ccf x=0;
while(c<48||c>57)q=c==45?-1:q,c=getchar();
while(c>=48&&c<=57)x=x*10+c-48,c=getchar();
return q==-1?-x:x;}
TMP inline ccf Max(ccf a,ccf b){return a<b?b:a;}
TMP inline ccf Min(ccf a,ccf b){return a<b?a:b;}
TMP inline ccf Max(ccf a,ccf b,ccf c){return Max(a,Max(b,c));}
TMP inline ccf Min(ccf a,ccf b,ccf c){return Min(a,Min(b,c));}
TMP inline ccf READ(ccf* _arr,int _n){RP(t,1,_n)_arr[t]=qr((ccf)1);}
//----------------------template&IO---------------------------
const int maxn=1e5+15;
struct E{
int to,nx,w;
}e[maxn<<1];
int n,m;
int F[maxn],T[maxn];
int head[maxn];
int cnt;
inline void add(int fr,int to,int w,bool f){
e[++cnt]=(E){to,head[fr],w};
head[fr]=cnt;
if(f) add(to,fr,w,0);
}
bool usd[maxn];
int d[maxn];
int spc[maxn];
int siz[maxn];
int sum,rt;
int dan[maxn];
int fans=0x3f3f3f3f;
int q[maxn];
inline void can(){
printf("%d\n",fans);
exit(0);
}
void getroot(int now,int last){
siz[now]=spc[now]=1;
ERP(t,now){
if(!usd[e[t].to]&&e[t].to!=last){
getroot(e[t].to,now);
siz[now]+=siz[e[t].to];
spc[now]=Max(spc[now],siz[e[t].to]);
}
}
spc[now]=Max(spc[now],sum-siz[now]);
if(spc[now]<spc[rt]||!rt) rt=now;
}
void getdis(int now,int last,int ew,int id){
dan[now]=id;
d[now]=d[last]+ew;
ERP(t,now){
if(e[t].to!=last){
getdis(e[t].to,now,e[t].w,id);
}
}
}
inline int calc(int now){
dan[now]=now;d[now]=0;
ERP(t,now) getdis(e[t].to,now,e[t].w,e[t].to);
register int top=0;
register int ans=0;
register int ret=0;
RP(t,1,m)
if(d[F[t]]+d[T[t]]==ans) q[++top]=t;
else if(d[F[t]]+d[T[t]]>ans) ans=d[F[t]]+d[T[t]],q[top=1]=t;
fans=Min(fans,ans);
RP(t,1,top){
if(dan[F[q[t]]]!=dan[T[q[t]]]) can();
else{
if(!ret) ret=dan[F[q[t]]];
if(ret!=dan[F[q[t]]]) can();
}
}
return ret;
}
void divd(int now){
usd[now]=1;
register int t=calc(now);
if(usd[t]) can();
sum=siz[t];rt=0;
getroot(t,0);
divd(rt);
}
int main(){
#ifndef ONLINE_JUDGE
freopen("in.in","r",stdin);
freopen("out.out","w",stdout);
#endif
sum=n=qr(1);
m=qr(1);
register int t1,t2,t3;
RP(t,1,n-1){
t1=qr(1);t2=qr(1);t3=qr(1);
add(t1,t2,t3,1);
}
RP(t,1,m)
F[t]=qr(1),T[t]=qr(1);
usd[0]=1;
getroot(1,0);
divd(rt);
cout<<fans<<endl;
return 0;
}
【题解】P4886快递员的更多相关文章
- [P4886] 快递员
考虑在树上选个点rt作为根,并且快递中心就选这儿.计算出所有配送的代价(2*两段之和),设他们的最大值为Max.若此时存在下列情况时,可以判定Max已经为最优解. 1)存在代价为Max的配送(u,v) ...
- [洛谷P4886]快递员
题目大意:一个$n$个点的树,树上有$m$个点对$(a,b)$,找到一个点$x$,使得$max(dis(x,a_i)+dis(x,b_i))$最小 如果做过幻想乡的战略游戏这道题,应该这道题的思路一眼 ...
- luogub P4886 快递员(点分治)
记得是9月月赛题,当时做的时候觉得跟ZJOI2015幻想乡战略游戏那道题很像???,就写了,然后就写挂了... 我们发现假设当前点为根,我们算出\(m\)次询问中最远的\(a\)对点,如果这\(a\) ...
- 【洛谷 P4886】 快递员 (点分治)
这题因为一些小细节还是\(debug\)了很久...不过我第一次用脚本对拍,不亏. 先随便找一个点作为根,算出答案,即所有点对到这个点的距离和的最大值,并记录所有距离最大的点对.如果这个点在任意一个距 ...
- 2019 湖南多校第一场(2018~2019NCPC) 题解
解题过程 开场shl过B,C,然后lfw写J,J WA了以后shl写A,但是因为OJ上空间开小WA了,而不是MLE?,J加了特判过了.之后一直在检查A错哪了,直到qt发现问题改了空间,浪费许多时间,但 ...
- POJ 2671 Jimmy's Bad Day题解(很详细很友好,类似区间dp)
有问题的话欢迎在评论区提出 题意: 题目链接 你是一个送快递的,现在给你一个环,环的边有权值,代表走这条边所花的时间,每个点代表一个地点,点有点权,代表这个点上有多少货物需要你送.初始时间\(t=0\ ...
- 2016 华南师大ACM校赛 SCNUCPC 非官方题解
我要举报本次校赛出题人的消极出题!!! 官方题解请戳:http://3.scnuacm2015.sinaapp.com/?p=89(其实就是一堆代码没有题解) A. 树链剖分数据结构板题 题目大意:我 ...
- noip2016十连测题解
以下代码为了阅读方便,省去以下头文件: #include <iostream> #include <stdio.h> #include <math.h> #incl ...
- BZOJ-2561-最小生成树 题解(最小割)
2561: 最小生成树(题解) Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 1628 Solved: 786 传送门:http://www.lyd ...
随机推荐
- Java分布式服务框架Dubbo初探(待实践)
Dubbo是什么? Dubbo[]是一个分布式服务框架,致力于提供高性能和透明化的RPC远程服务调用方案,以及SOA服务治理方案. 其核心部分包含: 远程通讯: 提供对多种基于长连接的NIO框架抽象封 ...
- mysql之日期函数
写在前面 mysql的学习,断断续续,今天就接着学习mysql的日期操作吧. 系列文章 mysql之创建数据库,创建数据表 mysql之select,insert,delete,update mysq ...
- Scut游戏服务器引擎6.1.5.6发布,直接可运行,支持热更新
1. 增加exe版(console),web版本(IIS)的游戏服宿主程序 2. 增加Model支持脚本化,实现不停服更新 3. 增加Language支持脚本化 4. 修改Sns与Pay Center ...
- Android BroadcastReceiver 注册和反注册
说起来这个问题很简单,只要注册和反注册成对出现就行,好像很多教材都是如此介绍.但实际开发中,对广播注册和反注册的时机把握还是很重要的. 关于广BroadcastReceiver注册和反注册时机,主要有 ...
- SCP和SFTP相同点和区别
都是使用SSH协议来传输文件的.不用说文件内容,就是登录时的用户信息都是经过SSH加密后才传输的,所以说SCP和SFTP实现了安全的文件传输. SCP和CP命令相似,SFTP和FTP的使用方法也类似. ...
- VS2010中 打开vc6.0的工程时,遇到的问题及解决办法
用VS2010打开vc6.0的工程时,遇到了很多的问题,下面记录下解决办法. 1. 报错: error C2065: “i”: 未声明的标识符 解决办法: 提出 i 的申明,因为 i 的申明可能在fo ...
- yii2操作数据库 mysql 读写分离 主从复制
转载地址:http://www.kuitao8.com/20150115/3471.shtml 开始使用数据库首先需要配置数据库连接组件,通过添加 db 组件到应用配置实现("基础的&quo ...
- Codis的安装
其他环境准备: 安装JDK,安装Zookeeper 1.创建codis账户 useradd codis passwd codis 2.解压codis3.1.3-go1.7.4-linux.tar.gz ...
- [javase学习笔记]-8.5 statickeyword的使用场景
这一节我们来看一下在我们开发的过程中,在什么时候我们要用到statickeyword进行静态修饰. 我们这里所说的静态.无非就是两种.一种是静态变量,一种是静态函数,我们分这两种情况进行说明stati ...
- 【oracle ocp知识点一】
1.怎样确定数据库是否启动 su - oracle ps -ef |grep ora_|head -2 两种关系数据库是ora或者是自己主动存储管理的asm开头的, 查看进程能够知道数据库实例至少已经 ...