1. 程式人生 > >2018.12.08【FJOI2014】【BZOJ4016】【洛谷P2993】最短路徑樹問題(最短路)(點分治)

2018.12.08【FJOI2014】【BZOJ4016】【洛谷P2993】最短路徑樹問題(最短路)(點分治)

BZOJ傳送門

洛谷傳送門


解析:

辛辛苦苦調了半天發現是最短路樹建錯了。。。

思路:

首先跑一個最短路,然後將每個點的所有出邊按照出點編號排序,DFS建出最短路樹。

這樣建樹才能夠保證字典序是最小的。(naive的我按照前驅結點建樹WA哭了)

那麼看一下題目要統計的東西,似乎是可以用點分治來做的。

注意這個統計的前提是最優化,所以不可以用容斥原理來做。

那麼對於每一個分治重心,按照子樹順序處理,每次處理子樹先查詢再更新。
保留距離重心 i

i 條邊的最遠點的距離,同時記錄滿足條件的點數。

剩下的就十分顯然了,看程式碼吧。


程式碼:

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define re register
#define gc getchar
#define pc putchar
#define cs const

inline int getint(){
	re int num;
	re char c;
	re bool f=0;
	while
(!isdigit(c=gc()))if(c=='-')f=1;num=c^48; while(isdigit(c=gc()))num=(num<<1)+(num<<3)+(c^48); return f?-num:num; } cs int N=30004,M=60004,INF=0x3f3f3f3f; vector<pair<int,int> > edge[N]; inline void addedge(int u,int v,int val){ edge[u].push_back(make_pair(v,val)); edge[v].push_back
(make_pair(u,val)); } int n,m,k; int dist[N]; inline void Dijkstra(){ memset(dist,0x3f,sizeof(dist)); set<pair<int,int> > q; q.insert(make_pair(0,1)); dist[1]=0; while(!q.empty()){ re int u=q.begin()->second; q.erase(q.begin()); for(int re e=0;e<edge[u].size();++e){ re int v=edge[u][e].first; if(dist[v]>dist[u]+edge[u][e].second){ q.erase(make_pair(dist[v],v)); dist[v]=dist[u]+edge[u][e].second; q.insert(make_pair(dist[v],v)); } } } } namespace SP_TREE{ int last[N],nxt[N<<1],to[N<<1],ecnt; int w[N<<1]; inline void addedge(int u,int v,int val){ nxt[++ecnt]=last[u],last[u]=ecnt,to[ecnt]=v,w[ecnt]=val; nxt[++ecnt]=last[v],last[v]=ecnt,to[ecnt]=u,w[ecnt]=val; } int ansd,ansc; int maxn,total,G; int siz[N]; bool ban[N]; inline void find_G(int u,int fa){ siz[u]=1; re int mx=0; for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){ if(v==fa||ban[v])continue; find_G(v,u); siz[u]+=siz[v]; mx=max(mx,siz[v]); } mx=max(mx,total-siz[u]); if(maxn>=mx)maxn=mx,G=u; } int maxl[N],cnt[N],dep[N]; inline void calc(int u,int fa){ if(maxl[k-dep[u]-1]!=-INF){ if(ansd<dist[u]+maxl[k-dep[u]-1])ansd=dist[u]+maxl[k-dep[u]-1],ansc=0; if(ansd==dist[u]+maxl[k-dep[u]-1])ansc+=cnt[k-dep[u]-1]; } if(dep[u]+1<k){ for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){ if(v==fa||ban[v])continue; dist[v]=dist[u]+w[e]; dep[v]=dep[u]+1; calc(v,u); } } } inline void update(int u,int fa){ if(maxl[dep[u]]<dist[u])maxl[dep[u]]=dist[u],cnt[dep[u]]=0; if(maxl[dep[u]]==dist[u])++cnt[dep[u]]; if(dep[u]+1<k){ for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){ if(v==fa||ban[v])continue; update(v,u); } } } inline void calc_G(int u){ ban[u]=true;maxl[0]=0;cnt[0]=1; for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){ if(ban[v])continue; dist[v]=w[e]; dep[v]=1; calc(v,0); update(v,0); } for(int re i=1;i<=k;++i)maxl[i]=-INF,cnt[i]=0; for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]]){ if(ban[v])continue; total=maxn=siz[v]; find_G(v,u); calc_G(G); } } inline void solve(){ memset(dist,0,sizeof dist); for(int re i=1;i<=k;++i)maxl[i]=-INF,cnt[i]=0; maxn=total=n; find_G(1,0); calc_G(G); printf("%d %d",ansd,ansc); } } bool vis[N]; inline void build(int u){ vis[u]=true; for(int re e=0;e<edge[u].size();++e) if(!vis[edge[u][e].first]&&dist[edge[u][e].first]==dist[u]+edge[u][e].second){ SP_TREE::addedge(u,edge[u][e].first,edge[u][e].second); build(edge[u][e].first); } } signed main(){ n=getint(),m=getint(),k=getint(); for(int re i=1;i<=m;++i){ int u=getint(),v=getint(),w=getint(); addedge(u,v,w); } for(int re i=1;i<=n;++i)sort(edge[i].begin(),edge[i].end()); Dijkstra(); build(1); SP_TREE::solve(); return 0; }