[SDOI2013]直径-解题报告

i207M

2019-04-12 16:36:06

Solution

发现自己的做法和其他题解的都不一样,感觉自己的做法更简单,细节更少一点,不用考虑各种情况。 回想最短路的必经边如何求?我们统计S到T的最短路条数,如果一条边$cnt[S][u]\times cnt[v][T]=cnt[S][T]$,那么这条边就是必经边。 这道题同理,我们可以两次dfs时树形DP,求出dp值和最优解的方案数,如果由这条边构成的直径的方案数等于全局方案数,那么这条边就必选。 ```cpp #define N 200005 int n; int head[N],cnte=1,v[N*2],nx[N*2],w[N*2]; il void adde(int uu,int vv,int ww) { v[++cnte]=vv,nx[cnte]=head[uu],head[uu]=cnte,w[cnte]=ww; } struct Data { LL w,cnt; Data() {} Data(const LL &_w,const LL &_cnt) {w=_w,cnt=_cnt;} il void comb(const Data &v) { if(v.w>w) w=v.w,cnt=v.cnt; else if(v.w==w) cnt+=v.cnt; } }; Data f[N],D,g[N]; int fe[N]; void dfs(int x,int _fa) { f[x].w=0,f[x].cnt=1; for(ri i=head[x]; i; i=nx[i]) { if(v[i]==_fa) continue; fe[v[i]]=w[i]; dfs(v[i],x); D.comb(Data(f[x].w+f[v[i]].w+w[i],f[x].cnt*f[v[i]].cnt)); f[x].comb(Data(f[v[i]].w+w[i],f[v[i]].cnt)); } } int ans; Data qz[N],hz[N]; il void check(const Data &v) { if(D.w==v.w&&D.cnt==v.cnt) ++ans; } void efs(int x,int _fa) { if(!_fa) g[x].cnt=1; else { g[x]=Data(g[_fa].w+fe[x],g[_fa].cnt); g[x].comb(Data(qz[x].w+fe[x],qz[x].cnt)),g[x].comb(Data(hz[x].w+fe[x],hz[x].cnt)); check(Data(g[x].w+f[x].w,g[x].cnt*f[x].cnt)); } static int st[N]; int tp=0; Data t; t.w=t.cnt=0; for(ri i=head[x]; i; i=nx[i]) { if(v[i]==_fa) continue; st[++tp]=v[i]; qz[v[i]]=t; t.comb(Data(f[v[i]].w+w[i],f[v[i]].cnt)); } t.w=t.cnt=0; for(ri i=tp; i>=1; --i) { hz[st[i]]=t; t.comb(Data(f[st[i]].w+fe[st[i]],f[st[i]].cnt)); } for(ri i=head[x]; i; i=nx[i]) { if(v[i]==_fa) continue; efs(v[i],x); } } signed main() { #ifdef M207 freopen("in.in","r",stdin); // freopen("ot.out","w",stdout); #endif in(n); for(ri i=1,a,b,c; i<n; ++i) { in(a,b,c); adde(a,b,c),adde(b,a,c); } dfs(1,0); efs(1,0); out(D.w); out(ans); return 0; } ```