本来抄了篇题解,后来觉得题解都太不友好(我太菜了),一气之下自己打。。。一打打到第二天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. COM对象创建过程

    在客户端需要调用COM组件时,通常调用windowAPI函数: STDAPI CoCreateInstance( REFCLSID rclsid, //创建的Com对象的类标识符(CLSID) LPU ...

  2. BZOJ3991:寻宝游戏 (LCA+dfs序+树链求并+set)

    小B最近正在玩一个寻宝游戏,这个游戏的地图中有N个村庄和N-1条道路,并且任何两个村庄之间有且仅有一条路径可达.游戏开始时,玩家可以任意选择一个村庄,瞬间转移到这个村庄,然后可以任意在地图的道路上行走 ...

  3. TCP/IP--VLSM

    ##########################VLSM################### 实际案例: 192.168.1.0/28 有几个网络号:2的4次方:16个网络号 : 有几个主机号: ...

  4. eclipse 通过maven 开发storm项目

    window系统安装java 首先我们需要下载java开发工具包JDK,下载地址:http://www.oracle.com/technetwork/java/javase/downloads/ind ...

  5. poj3281Dining——网络流匹配

    题目:http://poj.org/problem?id=3281 网络流做图中的匹配,注意为了限制每头牛只匹配一种食物和一种饮料,要把牛拆成两个点,之间连边权为1的边. 代码如下: #include ...

  6. webAPP meta 标签大全

    1.先说说mate标签里的viewport: viewport即可视区域,对于桌面浏览器而言,viewport指的就是除去所有工具栏.状态栏.滚动条等等之后用于看网页的区域.对于传统WEB页面来说,9 ...

  7. UDK游戏打包详解

    转自:http://blog.sina.com.cn/s/blog_944177030100ycki.html 安装完的udk目录下有4个主要的文件夹 Binaries -这个文件夹包含游戏的exe程 ...

  8. url&nbsp;传递参数(特殊字符)解决方法

    url 传递参数(特殊字符)解决方法 首先设置 apache 配置文件, server.xml 在 port=8080 那一行中加上 URIEcoding=GBK 有些符号在URL中是不能直接传递的, ...

  9. 《Java多线程编程核心技术》读后感(三)

    synchronized同步语句块 用synchronized声明方法在某些情况下是有弊端的,比如A线程调用同步方法执行以一个长时间的任务,那么B线程则必须等待比较较长的时间.在这样的情况下可以使用s ...

  10. 怎么查看linux系统是32还是64

    1.getconf LONG_BIT or getconf WORD_BIT例如:2.file /bin/ls例如: 查看linux的版本: