1. 程式人生 > >JZOJ 1005【GDOI2009五校聯考】公交網路【難】

JZOJ 1005【GDOI2009五校聯考】公交網路【難】

這題還是有點難度的(像我這種caiji都能推出來的難度)

網上都沒題解 這題放在OJ上9年了都 才9個人做 (好吧是dalao們不想做)

於是經過本弱三天(一天一小時健康一輩子)的努力 把這題弄掉了QAQ

好了先放題目

Description

在世界某個安靜的角落,N 只毛毛蟲幸福地生活在那裡。但它們並不住在一起。每隻毛毛蟲都有各自不同的窩,編號便是1 至N,窩之間也連著一些道路。相信你也已經想到了,由於生活空間的有限,任意一對窩之間僅存在一條簡單路徑。不過毛毛蟲們很團結,它們願意不停地在彼此之間聯絡,因此任意一隻毛毛蟲都可以通過那些道路組成的路徑拜訪其餘所有的夥伴。日子久了,毛毛蟲們發現,每次拜訪朋友都要很艱難地爬行一大段路。於是,它們決定在自己的家園建起一個無蟲駕駛的公交網路。它們決定先找一個窩建一個公交總站,而其他所有的窩都向公交總站發出一班往返的公交車,沿路經過的所有窩都作為這條公交線路的一個旅客站,方便它們在任意站點下車。這下可樂壞了毛毛蟲們,因為拜訪朋友更方便了。我們可以定義一對毛毛蟲之間的距離,就是其中一隻毛毛蟲去拜訪另一隻最少需要乘坐的公交車數(即在一輛公交車上不論坐多久都只算一次)。現在,毛毛蟲們想知道,最優情況下,任意一對毛毛蟲之間的距離總和最小是多少。而問題的關鍵就在於如何選取公交總站了,這當然就交給你來完成咯~~注意(A,B)和(B,A)只需算一次。

Input

第一行,一個正整數N,即毛毛蟲數。
第二行起共N-1 行,每行兩個正整數Ai、Bi,表示有道路直接相連的兩個窩的編號。

Output

僅一行一個數,表示你要求的答案。

Sample Input

4
1 2 
1 3 
1 4

Sample Output

7

Hint

對於100% 的資料,有N ≤ 1 000 000

 

首先講講題意

就是選個樹根 然後記錄每個點到其他點的距離 這裡距離是這樣算的——

設當前點為 p 其他點之一是 b 則

若 b 在 p 的子樹內 b 和 p 的距離為 1 這個用 siz 可以記錄下來

若 b 在 p 到根的路徑上 b 和 p的距離為 1 這個用 dep 可以記錄下來

其他情況 b 和 p 的距離為 2 當然點不能重合啦

所以當前點 p 到其他點的距離為 (siz[p] + dep[p] - 1 ——這是距離為1的點的 減一是因為siz包括p點) + ((n - siz[a] - dep[a]) * 2 ——這是距離為2的 總點數減去就好啦)

 

20分做法:考慮暴力 每個點當成根算一次 外面一層dfs 裡面嵌一層更新深度的dfs 更新答案 然後遞迴的時候要換根和其某一子節點的關係 就像這樣

{
	fa[p] = b,fa[b] = 0;
	siz[p] -= siz[b],siz[b] = n;
	dfs2(b);
	siz[b] -= siz[p],siz[p] = n;
	fa[b] = p,fa[p] = 0;
}

70分做法:考慮到更新後可能答案會變大 然後通過更新發現 設當前根為 p 新根為b 則我們得到兩次答案差為

(newsiz[p] + newdep[p] - 1 + ((n - newsiz[p] - newdep[p]) * 2)) + 
(newsiz[b] + newdep[b] - 1 + ((n - newsiz[b] - newdep[b]) * 2)) -
(siz[p] + dep[p] - 1 + ((n - siz[p] - dep[p]) * 2)) -
(siz[b] + dep[b] - 1 + ((n - siz[b] - dep[b]) * 2))

然後顯然 newp = b, newb = p, dep[p] = 0 使 newdep[newp] = 1, dep[b] = 1 使 newdep[newb] = 0

於是我們就可以簡化成這樣一個鬼東西 \Delta ans = siz[b] - newsiz[p]

那麼顯然如果 \Delta ans 小於0的話 更新的答案就比原來小 那麼我們如果更新的 \Delta ans < 0 就嵌個getans的dfs進去

 

70分做法:還是上面那個 但是上面那個跑後面三個點會RE

dfs50W個直接爆棧了啦QAQ 然後我們改成手動棧 (要改三個累死人)

好了 TLE

 

100分做法:我們考慮到更新答案更更更更更然後總會有個最小的

那我們能不能把中間的更新省掉?答案是可以的

於是我跑了個大資料搞出這麼個鬼東西

然後發現只用更新 得到 mndelta 時的點為根 (我天真地用了drta) 和初始根答案的差——ans(root(1)) + mndelta

於是愉快地搞定了

Upd:還是放下巨醜無比的程式碼吧 =-=

#include <cstring>
#include <cstdio>
#define MAXN 1000005
#define ll long long
struct edge {
	int ne,to;
} e[MAXN << 1],que[MAXN];
int first[MAXN],fa[MAXN],siz[MAXN],dep[MAXN],cur[MAXN],due[MAXN],tot,n;
short o[MAXN];
ll ans = (ll)MAXN * MAXN * 2,drta,mndrta;
inline ll min(ll x,ll y) {return x < y ? x : y;}
inline int r()
{
	char q = getchar(); int x = 0;
	while (q < '0' || q > '9') q = getchar();
	while ('0' <= q && q <= '9')
		x = (x << 3) + (x << 1) + q - (3 << 4),q = getchar();
	return x;
}
inline void add(int x,int y)
{
	e[++tot].ne = first[x];
	e[tot].to = y;
	first[x] = tot;
}
inline ll getans()
{
	ll bot = 0;
	for (int a = 1 ; a <= n ; ++ a)
		bot += (ll)siz[a] + dep[a] - 1 + ((n - siz[a] - dep[a]) << 1);
	return bot / 2;
}
int main()
{
	int x,y; n = r();
	for (int a = 1 ; a <  n ; ++ a) x = r(),y = r(),add(x,y),add(y,x);
	for (int a = 1 ; a <= n ; ++ a) ++siz[a];
	que[tot = 1].ne = 1;
	while (tot)
	{
		int p = que[tot].to;
		int b = que[tot].ne;
		cur[b] = o[b] ? e[cur[b]].ne : first[b];
		o[b] = 1;
		if (o[e[cur[b]].to]) cur[b] = e[cur[b]].ne;
		if (!cur[b]) {siz[p] += siz[b]; --tot; continue;}
		int d = e[cur[b]].to;
		que[++tot].to = b;
		que[tot].ne = d;
		fa[d] = b;
	}
	due[1] = 1;
	int h = 0,t = 1;
	while (h < t)
	{
		int p = due[++h];
		for (int a = first[p],b = e[a].to ; a ; a = e[a].ne,b = e[a].to)
			if (b != fa[p]) dep[b] = dep[p] + 1,due[++t] = b;
	}
	ans = getans();
	memset(que,0,sizeof(que));
	memset(o,0,sizeof(o));
	que[tot = 1].ne = 1;
	while (tot)
	{
		int p = que[tot].to;
		int b = que[tot].ne;
		mndrta = min(mndrta,drta);
		cur[b] = o[b] ? e[cur[b]].ne : first[b];
		o[b] = 1;
		if (o[e[cur[b]].to]) cur[b] = e[cur[b]].ne;
		if (!cur[b])
		{
			drta += (ll)siz[p];
			siz[b] -= siz[p],siz[p] = n,drta -= (ll)siz[b];
			fa[b] = p,fa[p] = 0,--tot;
			continue;
		}
		int d = e[cur[b]].to;
		fa[b] = d,fa[d] = 0,drta += (ll)siz[d];
		siz[b] -= siz[d],siz[d] = n,drta -= (ll)siz[b];
		que[++tot].to = b;
		que[tot].ne = d;
	}
	printf("%lld\n",ans + mndrta);
	return 0;
}