题解 BZOJ 1912 && luogu P3629 [APIO2010]巡逻 (树的直径)
本来抄了篇题解,后来觉得题解都太不友好(我太菜了),一气之下自己打。。。一打打到第二天QAQ
首先什么边也不加时,总路程就是2*(n-1)
考虑k=1的时候,答案显然是2*(n-1)-直径+1=2*n-直径-1,如果能加一条边的话,因为希望减少的尽可能多,那么只需要把直径的首尾接起来,就不需要来回走,加一就是加了这一条新加入的边。
而k=2的时候,首先还是往最长链上面思考。然而做k=1的时候已经用掉了一段,我们需要k=2的和k=1的不重叠。
于是乎,我们跑完直径后之后把直径上的边权全部修改为-1,再跑一遍直径就可以了。那权值为-1的边又被选了就是考虑第一次算这条边的时候加了1,第二次的时候是-1,相当于是这条边没有产生任何贡献。所以最后答案是2(n-1)-(直径1-1)-(直径2-1)=2n-直径1-直径2
哪位大佬能教教我DP记路径吗。。。。感激不尽( ⊙ o ⊙ )啊!
我的傻乎乎的代码
#include<cstdio>
#include<iostream>
#include<cstring>
#define R register int
using namespace std;
const int N=,Inf=0x3f3f3f3f;
inline int g() {
R ret=,fix=; register char ch; while(!isdigit(ch=getchar())) fix=ch=='-'?-:fix;
do ret=ret*+(ch^); while(isdigit(ch=getchar())); return ret*fix;
}
int n,k,mx,mx1,cnt,st,ed;
int pre[N],fir[N],cnte[N],d[N];
struct edge{
int v,w,nxt;
#define v(i) e[i].v
#define w(i) e[i].w
#define nxt(i) e[i].nxt
}e[N<<];
inline void add(int u,int v,int w) {v(++cnt)=v,w(cnt)=w,nxt(cnt)=fir[u],fir[u]=cnt;}
namespace _dp {
void dp(int u,int fa) {
for(R i=fir[u];i;i=nxt(i)) {
R v=v(i);
if(v==fa) continue;
dp(v,u);
mx=max(mx,d[u]+d[v]+w(i));
d[u]=max(d[u],d[v]+w(i));
}
}
inline void solve() {
memset(d,,sizeof(d));
dp(,);
}
}
inline void dfs(int u,int fa) {
for(R i=fir[u];i;i=nxt(i)) {
R v=v(i);
if(v==fa) continue;
d[v]=d[u]+w(i);
dfs(v,u);
}
}
inline void solve() {
memset(d,0x3f,sizeof(d));
d[]=; dfs(,); mx=-Inf; st=,ed=;
for(R i=;i<=n;++i) if(d[i]>mx&&d[i]!=Inf&&i!=)
mx=d[i],st=i;
memset(d,0x3f,sizeof(d));
d[st]=,dfs(st,); mx=-Inf;
for(R i=;i<=n;++i) if(d[i]>mx&&d[i]!=Inf&&i!=st)
mx=d[i],ed=i;
}
inline void fd_p(int u,int fa) {
for(R i=fir[u];i;i=nxt(i)) {
R v=v(i);
if(v==fa) continue;
fd_p(v,u);
pre[v]=u;
cnte[v]=i;
}
}
signed main() {
n=g(),k=g();
for(R i=,u,v;i<n;++i) u=g(),v=g(),add(u,v,),add(v,u,);
solve();
if(k==) {printf("%d\n",*n-mx-); return ;}
fd_p(st,); mx1=mx; pre[st]=;
for(R i=ed;i;i=pre[i]) if(cnte[i]&) w(cnte[i])=-,w(cnte[i]+)=-; else w(cnte[i])=-,w(cnte[i]-)=-;
mx=;
_dp::solve();
printf("%d\n",*n-mx1-mx);
}
我看不懂的代码(fromljh2000%%%%%)
#include<cstdio>
#include<iostream>
#include<cstring>
#define R register int
using namespace std;
const int N=;
inline int g() {
R ret=,fix=; register char ch; while(!isdigit(ch=getchar())) fix=ch=='-'?-:fix;
do ret=ret*+(ch^); while(isdigit(ch=getchar())); return ret*fix;
}
int n,k,cnt,anss,ans,rt,S,SP;
int fir[N],nxt[N],nxte[N],f[N][];
struct edge{
int v,w,nxt;
#define v(i) e[i].v
#define w(i) e[i].w
#define nxt(i) e[i].nxt
}e[N<<];
inline void add(int u,int v,int w) {v(++cnt)=v,w(cnt)=w,nxt(cnt)=fir[u],fir[u]=cnt;}
inline void dfs(int u,int fa) {
R crt,s=,sp=;
for(R i=fir[u];i;i=nxt(i)) {
R v=v(i);
if(v==fa) continue;
dfs(v,u);
crt=f[v][]+w(i);
if(crt>f[u][]) s=nxt[u],sp=nxte[u],f[u][]=f[u][],f[u][]=crt,nxt[u]=v,nxte[u]=i;
else if(crt>f[u][]) f[u][]=crt,s=v,sp=i;
}
if(f[u][]+f[u][]>ans) {
ans=f[u][]+f[u][]; rt=u,S=s,SP=sp;
}
}
signed main() {
n=g(),k=g(); R x;
for(R i=,u,v;i<n;++i) u=g(),v=g(),add(u,v,),add(v,u,);
dfs(,);
anss=*(n-)-ans+;
if(k==) {printf("%d\n",anss); return ;}
if(f[rt][]>) {
x=S; w(SP)=-;
while(nxt[x]) w(nxte[x])=-,x=nxt[x];
}
x=rt;
while(nxt[x]) w(nxte[x])=-,x=nxt[x];
ans=; memset(f,,sizeof(f));
dfs(,); anss-=ans-; printf("%d\n",anss);
}
2019.04.02
题解 BZOJ 1912 && luogu P3629 [APIO2010]巡逻 (树的直径)的更多相关文章
- 树的直径初探+Luogu P3629 [APIO2010]巡逻【树的直径】By cellur925
题目传送门 我们先来介绍一个概念:树的直径. 树的直径:树中最远的两个节点间的距离.(树的最长链)树的直径有两种方法,都是$O(N)$. 第一种:两遍bfs/dfs(这里写的是两遍bfs) 从任意一个 ...
- 洛谷 P3629 [APIO2010]巡逻 解题报告
P3629 [APIO2010]巡逻 题目描述 在一个地区中有 n 个村庄,编号为 1, 2, ..., n.有 n – 1 条道路连接着这些村 庄,每条道路刚好连接两个村庄,从任何一个村庄,都可以通 ...
- 洛谷P3629 [APIO2010]巡逻(树的直径)
如果考虑不算上新修的道路,那么答案显然为\(2*(n-1)\). 考虑\(k=1\)的情况,会发现如果我们新修建一个道路,那么就会有一段路程少走一遍.这时选择连接树的直径的两个端点显然是最优的. 难就 ...
- [洛谷P3629] [APIO2010]巡逻
洛谷题目链接:[APIO2010]巡逻 题目描述 在一个地区中有 n 个村庄,编号为 1, 2, ..., n.有 n – 1 条道路连接着这些村 庄,每条道路刚好连接两个村庄,从任何一个村庄,都可以 ...
- P3629 [APIO2010]巡逻
题目描述 在一个地区中有 n 个村庄,编号为 1, 2, ..., n.有 n – 1 条道路连接着这些村 庄,每条道路刚好连接两个村庄,从任何一个村庄,都可以通过这些道路到达其 他任一个村庄.每条道 ...
- 洛谷 P3629 [APIO2010]巡逻
题目在这里 这是一个紫题,当然很难. 我们往简单的想,不建立新的道路时,从1号节点出发,把整棵树上的每条边遍历至少一次,再回到1号节点,会恰好经过每条边两次,路线总长度为$2(n-1)$,根据树的深度 ...
- luogu题解 P1099 【树网的核】树的直径变式+数据结构维护
题目链接: https://www.luogu.org/problemnew/show/P1099 https://www.lydsy.com/JudgeOnline/problem.php?id=1 ...
- Luogu 3629 [APIO2010]巡逻
先考虑$k = 1$的情况,很明显每一条边都要被走两遍,而连成一个环之后,环上的每一条边都只要走一遍即可,所以我们使这个环的长度尽可能大,那么一棵树中最长的路径就是树的直径. 设直径的长度为$L$,答 ...
- 【BZOJ-1912】patrol巡逻 树的直径 + DFS(树形DP)
1912: [Apio2010]patrol 巡逻 Time Limit: 4 Sec Memory Limit: 64 MBSubmit: 1034 Solved: 562[Submit][St ...
随机推荐
- netstat参数记录
可以使用man netstat查看TCP的各种状态信息描述 ESTABLISHED socket已经建立连接 CLOSED socket没有被使用,无连接 CL ...
- YYYY-mm-dd HH:MM:SS 备忘录
d 月中的某一天.一位数的日期没有前导零. dd 月中的某一天.一位数的日期有一个前导零. ddd 周中某天的缩写名称,在 AbbreviatedDayNames 中定义. dddd 周中某天的完整名 ...
- Mybatis异常_02_Result Maps collection already contains value for
一.异常 1.异常信息 2.异常原因 XXXMapper.xml文件中存在重名对象,保持名称不要一样即可正常启动. 我的原因是namespace与其他mapper 一样. 3.可能的原因 (1)nam ...
- 一个关于前端页面的小标签<tbody>
我们有时候希望将表格的内容分为多个模块,这时候就可以使用<tbody>标签,它是<table>的字标签,是<tr>的父标签,可以使用它达到一种设置样式的结果.
- codeforces 660D D. Number of Parallelograms(计算几何)
题目链接: D. Number of Parallelograms time limit per test 4 seconds memory limit per test 256 megabytes ...
- linux 进程学习笔记-共享内存
如果能划定一块物理内存,让多个进程都能将该内存映射到其自身虚拟内存空间的话,那么进程可以通过向这块内存空间读写数据而达到通信的目的.另外,和消息队列不同的是,共享的内存在用户空间而不是核空间,那么就不 ...
- bzoj3462DZY Loves Math II
数据范围:$$2 \leq S \leq 2 * 10^6$$ $$1 \leq n \leq 10^{18}$$ $$ 1 \leq q \leq 10^5$$ 数学+dp 题解写一年系列... 观 ...
- MongoDB4.0.0的安装配置—windows
一.背景 由于要学习MongoDB,所以就下载了最新的MongoDB 的Community Server版的4.0.0版本.可能是新的版本的缘故,在安装配置上与MongoDB3有许多不同,而且在3中的 ...
- Redis 客户端安装与远程连接图解
Linux环境:Centos 6.8 Redis服务端版本:3.2.6 Redis客户端下载链接:https://redisdesktop.com/download 省略Linux系统安装Redis教 ...
- leetcode Word Break-单词划分
题目描述: 给定一个字符串s和一组单词,确定这个字符串是否能够进行这样一种划分,划分后所有的子字符串均来自给定的单词组.例如s = “leetcode” ,dict = {“leet”,“code”} ...