給乙個包含n個點,m條邊的無向連通圖。從頂點1出發,往其餘所有點分別走一次並返回。
往某乙個點走時,選擇總長度最短的路徑走。若有多條長度最短的路徑,則選擇經過的頂點序列字典序最小的那條路徑(如路徑a為1,32,11,路徑b為1,3,2,11,路徑b字典序較小。注意是序列的字典序的最小,而非路徑中節點編號相連的字串字典序最小)。到達該點後按原路返回,然後往其他點走,直到所有點都走過。
可以知道,經過的邊會構成一棵最短路徑樹。請問,在這棵最短路徑樹上,最長的包含k個點的簡單路徑長度為多長?長度為該最長長度的不同路徑有多少條?
這裡的簡單路徑是指:對於乙個點最多隻經過一次的路徑。不同路徑是指路徑兩端端點至少有乙個不同,點a到點b的路徑和點b到點a視為同一條路徑。
第一行輸入三個正整數n,m,k,表示有n個點m條邊,要求的路徑需要經過k個點。接下來輸入m行,每行三個正整數ai,bi,ci(1<=ai,bi<=n,1<=ci<=10000),表示ai和bi間有一條長度為ci的邊。資料保證輸入的是連通的無向圖。
輸出一行兩個整數,以乙個空格隔開,第乙個整數表示包含k個點的路徑最長為多長,第二個整數表示這樣的不同的最長路徑有多少條。
題目大意大概就是要根據規定的一些條件建樹,然後要求出2個問題,注意第二問長度為該長度的路徑也必須包含k個點。
建圖的話,首先跑一遍最短路,然後對於乙個點u,從小到大列舉與其相鄰每個點,若是該點在最短路圖上,則建邊樹。
然後進行樹分治,首先我們開乙個陣列a[i]儲存經過i條邊的路徑的最大長度,用b[i][j]表示經過i條邊路徑長度為j的路徑條數,然後對於乙個點的每棵子樹單獨處理,現在我們考慮合併的情況,假設我們已知前i-1棵子樹的資訊,然後我們搜尋第i棵子樹,用dis表示經過的邊數,用dist表示經過的路徑長度,若是存在a[k - dis[u] - 1],則判斷大小,更新,在保證路徑長度最長的情況下更新邊數。由於map陣列b速度非常慢(在windows下最慢的點跑了1.40s),我們可以用乙個權值線段樹來優化,時間複雜度o(n log ^ 2(n)).
(當然我的做法是偏複雜的,存在o(n log n)的做法,請讀者自行思考)
附上**:
#include#include#include#include#include#includeusing namespace std;
const int maxx = 60005;
const int inf = 300000005;
const int maxn = 6000005;
int first[maxx], next[maxx << 1], go[maxx << 1], way[maxx << 1], t, ans, ans1;
int first1[maxx], next1[maxx << 1], go1[maxx << 1], way1[maxx << 1], a[maxx];
int n, i, j, k, l, m, k, root, sroot, size[maxx];
int dis[maxx], q[maxx << 5], w, dist[maxx], tot;
int sum[maxn], lc[maxn], rc[maxn], len, rt[maxx];
bool e[maxx], vis[maxx];
struct sb;
sb sta[maxx << 1];
inline bool rule(const sb &a, const sb &b)
inline int get()
inline void add1(const int &x, const int &y, const int &z)
inline void add(const int &x, const int &y, const int &z)
inline int max(const int &x, const int &y)
inline void bfs()
} e[k] = 0; }}
inline void dfs1(const int &now) }}
inline void insert(int &k, const int &p, const int &q, const int &w, int ww)
int mid = (p + q) >> 1;
if (mid >= w) insert(lc[k], p, mid, w, ww);
if (mid < w) insert(rc[k], mid + 1, q, w, ww);
}inline void find(const int &k, const int &p, const int &q, const int &w)
int mid = (p + q) >> 1;
if (mid >= w) find(lc[k], p, mid, w);
if (mid < w) find(rc[k], mid + 1, q, w);
}inline void getroot(const int &now, const int &fa, int p)
inline void getdis(const int &now, const int &fa)
inline void getans(const int &now, const int &fa)
else if (k - dis[now] - 1 > 0 && a[k - dis[now] - 1] && ans - dist[now] > 0)
for(int i = first[now]; i; i = next[i])
if (go[i] != fa && !vis[go[i]]) }
inline void delet(const int &now, const int &fa)
inline void solve(const int &now)
for(int i = first[now]; i; i = next[i])
if (!vis[go[i]]) delet(go[i], now);
for(int i = first[now]; i; i = next[i])
if (!vis[go[i]]) }
int main()
sort(sta + 1, sta + 1 + m + m, rule);
for(i = 1; i <= (m << 1); i ++)
add1(sta[i].x, sta[i].y, sta[i].z);
t = 0;
bfs();
dfs1(1);
for(i = 1; i <= n; i ++)
vis[i] = dis[i] = 0;
sroot = maxx;
getroot(1, 0, n);
solve(root);
cout << ans << " " << ans1 << endl;
}
FJOI2014 最短路徑樹問題
here 吐槽一下這個題,完全就是兩個裸題拼一起了,而且兩個板子之間毫無聯絡 首先我們造乙個保證字典序最小的最短路徑樹,怎麼保證字典序呢,先將你存的圖按字典序從小到大重新排個序再跑最短路就行了。之後就是 跑 dijkstra dfs 一遍重新建圖,如果 u 已經被訪問過,那麼邊 e 在最短路上的當且...
FJOI2014 最短路徑樹問題
這題已經在我的收藏夾裡吃了大半年的灰了 主要是因為他們有人把這題歸到了樹形dp裡面,然後我就傻乎乎地把它收藏了 首先,假設我們已經求出了這個 最短路徑樹 剩下的就是點分治的板子了。而這個 最短路徑樹 首先可以通過dijkstra跑出最短路徑dag,然後在dag上用bfs即可求出字典序最小的樹。總的來...
樹的分治 FJOI2014最短路徑樹問題
題目大意 給乙個包含n個點,m條邊的無向連通圖。從頂點1出發,往其餘所有點分別走一次並返回。往某乙個點走時,選擇總長度最短的路徑走。若有多條長度最短的路徑,則選擇經過的頂點序列字典序最小的那條路徑 如路徑a為1,32,11,路徑b為1,3,2,11,路徑b字典序較小。注意是序列的字典序的最小,而非路...