题解 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\n",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\n",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\n",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\n",anss); }
2019.04.02