题解 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跑直径,不要像我一样傻乎乎的用dfs(妄想dfs一石二鸟,何不如直接全DP(但我不会用DP记路径))
 
对比一下大佬的代码:我的需要专门去找第一次直径的路径再修改,就是fd_p()函数,所以会跑得慢一些。。。。

哪位大佬能教教我DP记路径吗。。。。感激不尽( ⊙ o ⊙ )啊!

我的傻乎乎的代码

#include<cstdio>
#include<iostream>
#include<cstring>
#define R register int
using namespace std;
const int N=100010,Inf=0x3f3f3f3f;
inline int g() {
    R ret=0,fix=1; register char ch; while(!isdigit(ch=getchar())) fix=ch=='-'?-1:fix;
    do ret=ret*10+(ch^48); 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<<1];
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,0,sizeof(d));
        dp(1,0);
    }
}
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[1]=0; dfs(1,0); mx=-Inf; st=0,ed=0;
    for(R i=1;i<=n;++i) if(d[i]>mx&&d[i]!=Inf&&i!=1)
        mx=d[i],st=i;
    memset(d,0x3f,sizeof(d));
    d[st]=0,dfs(st,0); mx=-Inf;
    for(R i=1;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=1,u,v;i<n;++i) u=g(),v=g(),add(u,v,1),add(v,u,1);
    solve();
    if(k==1) {printf("%d
",2*n-mx-1); return 0;}
    fd_p(st,0); mx1=mx; pre[st]=0;
    for(R i=ed;i;i=pre[i]) if(cnte[i]&1) w(cnte[i])=-1,w(cnte[i]+1)=-1; else w(cnte[i])=-1,w(cnte[i]-1)=-1;
    mx=0;
    _dp::solve();
    printf("%d
",2*n-mx1-mx);
}

我看不懂的代码(from ljh2000%%%%%)

#include<cstdio>
#include<iostream>
#include<cstring>
#define R register int 
using namespace std;
const int N=100010;
inline int g() {
    R ret=0,fix=1; register char ch; while(!isdigit(ch=getchar())) fix=ch=='-'?-1:fix;
    do ret=ret*10+(ch^48); 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][2];
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<<1];
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=0,sp=0;
    for(R i=fir[u];i;i=nxt(i)) {
        R v=v(i);
        if(v==fa) continue;
        dfs(v,u);
        crt=f[v][0]+w(i);
        if(crt>f[u][0]) s=nxt[u],sp=nxte[u],f[u][1]=f[u][0],f[u][0]=crt,nxt[u]=v,nxte[u]=i;
        else if(crt>f[u][1]) f[u][1]=crt,s=v,sp=i;
    }
    if(f[u][0]+f[u][1]>ans) {
        ans=f[u][0]+f[u][1]; rt=u,S=s,SP=sp;
    }
}
signed main() {
    n=g(),k=g(); R x;
    for(R i=1,u,v;i<n;++i) u=g(),v=g(),add(u,v,1),add(v,u,1);
    dfs(1,0);
    anss=2*(n-1)-ans+1;
    if(k==1) {printf("%d
",anss); return 0;}
    if(f[rt][1]>0) {
        x=S; w(SP)=-1; 
        while(nxt[x]) w(nxte[x])=-1,x=nxt[x];
    }
    x=rt;
    while(nxt[x]) w(nxte[x])=-1,x=nxt[x];
    ans=0; memset(f,0,sizeof(f));
    dfs(1,0); anss-=ans-1; printf("%d
",anss);
}

2019.04.02

原文地址:https://www.cnblogs.com/Jackpei/p/10640217.html