本来抄了篇题解,后来觉得题解都太不友好(我太菜了),一气之下自己打。。。一打打到第二天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跑直径,不要像我一样傻乎乎的用dfs(妄想dfs一石二鸟,何不如直接全DP(但我不会用DP记路径))
 
对比一下大佬的代码:我的需要专门去找第一次直径的路径再修改,就是fd_p()函数,所以会跑得慢一些。。。。

哪位大佬能教教我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]巡逻 (树的直径)的更多相关文章

  1. 树的直径初探+Luogu P3629 [APIO2010]巡逻【树的直径】By cellur925

    题目传送门 我们先来介绍一个概念:树的直径. 树的直径:树中最远的两个节点间的距离.(树的最长链)树的直径有两种方法,都是$O(N)$. 第一种:两遍bfs/dfs(这里写的是两遍bfs) 从任意一个 ...

  2. 洛谷 P3629 [APIO2010]巡逻 解题报告

    P3629 [APIO2010]巡逻 题目描述 在一个地区中有 n 个村庄,编号为 1, 2, ..., n.有 n – 1 条道路连接着这些村 庄,每条道路刚好连接两个村庄,从任何一个村庄,都可以通 ...

  3. 洛谷P3629 [APIO2010]巡逻(树的直径)

    如果考虑不算上新修的道路,那么答案显然为\(2*(n-1)\). 考虑\(k=1\)的情况,会发现如果我们新修建一个道路,那么就会有一段路程少走一遍.这时选择连接树的直径的两个端点显然是最优的. 难就 ...

  4. [洛谷P3629] [APIO2010]巡逻

    洛谷题目链接:[APIO2010]巡逻 题目描述 在一个地区中有 n 个村庄,编号为 1, 2, ..., n.有 n – 1 条道路连接着这些村 庄,每条道路刚好连接两个村庄,从任何一个村庄,都可以 ...

  5. P3629 [APIO2010]巡逻

    题目描述 在一个地区中有 n 个村庄,编号为 1, 2, ..., n.有 n – 1 条道路连接着这些村 庄,每条道路刚好连接两个村庄,从任何一个村庄,都可以通过这些道路到达其 他任一个村庄.每条道 ...

  6. 洛谷 P3629 [APIO2010]巡逻

    题目在这里 这是一个紫题,当然很难. 我们往简单的想,不建立新的道路时,从1号节点出发,把整棵树上的每条边遍历至少一次,再回到1号节点,会恰好经过每条边两次,路线总长度为$2(n-1)$,根据树的深度 ...

  7. luogu题解 P1099 【树网的核】树的直径变式+数据结构维护

    题目链接: https://www.luogu.org/problemnew/show/P1099 https://www.lydsy.com/JudgeOnline/problem.php?id=1 ...

  8. Luogu 3629 [APIO2010]巡逻

    先考虑$k = 1$的情况,很明显每一条边都要被走两遍,而连成一个环之后,环上的每一条边都只要走一遍即可,所以我们使这个环的长度尽可能大,那么一棵树中最长的路径就是树的直径. 设直径的长度为$L$,答 ...

  9. 【BZOJ-1912】patrol巡逻 树的直径 + DFS(树形DP)

    1912: [Apio2010]patrol 巡逻 Time Limit: 4 Sec  Memory Limit: 64 MBSubmit: 1034  Solved: 562[Submit][St ...

随机推荐

  1. 在js实现矩阵转置

    var arr=[[2,4,6,8],[8,9,0,-1],[9,6,2,1]]; //定义一个新的数组 var arr2=[]; for(var i=0;i<arr[0].length;i++ ...

  2. bzoj 3685

    线段树 方法一: 值域线段树,递归去写的,每次节点存出现次数. 对于几个操作, 1,2 直接加减就好 ; 3,4 操作贪心往某一个方向找 .7也很简单,主要说前驱后继怎么找.我是先找这个数第几小,根据 ...

  3. listen 60

    Barbie Exposure May Limit Girls' Career Imagination The ubiquitous Barbie doll: she's been everythin ...

  4. C++ string的查找函数和npos特殊值

    STL中的string有6个查找函数: 1.find() 2.rfind() 从最后一个字符开始往前找. 3.find_first_of() 4.find_not_first_of() 5.find_ ...

  5. leetcode 67. Add Binary (高精度加法)

    Given two binary strings, return their sum (also a binary string). For example,a = "11"b = ...

  6. linux命令学习笔记(23):Linux 目录结构

    对于每一个Linux学习者来说,了解Linux文件系统的目录结构,是学好Linux的至关重要的一步.,深入了解linux文件 目录结构的标准和每个目录的详细功能,对于我们用好linux系统只管重要,下 ...

  7. NO1:安装VMLinux虚拟机,安装配置Samba实现Linux与Windows文件共享

    离开技术好些年,仅凭记忆开始学习.同时决定在Linux系统学习C语言. 一.下午安装了VM 8.0,安装RedHat Enterpris Server 6.4虚拟操作系统,都还比较顺利. 二.要实现L ...

  8. 使用mutt+msmtp做linux邮件客户端

      下载MSMTP wget http://nchc.dl.sourceforge.net/sourceforge/msmtp/msmtp-1.4.17.tar.bz2 tar xvf msmtp-1 ...

  9. Data Guard 异构平台支持手册

    背景 最简单的DG 环境是同数据库版本,同操作系统平台. 但有时需要在异构平台上来部署DG 环境,比如异构下使用DG 来进行数据迁移,从而降低停机时间和风险等等. 1. 查看主备库的Platform ...

  10. BZOJ1798:[AHOI2009]维护序列

    浅谈树状数组与线段树:https://www.cnblogs.com/AKMer/p/9946944.html 题目传送门:https://www.lydsy.com/JudgeOnline/prob ...