JZOJ 1005【GDOI2009五校联考】公交网络【难】

版权声明:并没卵用的版权声明~喜欢的就在评论区聊聊天吧~~ https://blog.csdn.net/Frocean/article/details/84168661

这题还是有点难度的(像我这种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;
}

猜你喜欢

转载自blog.csdn.net/Frocean/article/details/84168661