P4183 [USACO18JAN]Cow at Large P

题面自行看吧。。。

std

首先把当前奶牛的位置作为根。

gug_u 表示 uu 与最近的叶子的距离。

那么若奶牛在 uu,无法走到 ii 的子树内,当且仅当 dis(u,i)gi\operatorname{dis}(u, i) \ge g_i

由于要使答案最优,即让 ii 管辖的节点尽可能多,若 ii 满足上面条件,faifa_i 也满足上面条件,那么优先选 faifa_i

那么上面条件可转化为:dis(u,i)gi\operatorname{dis}(u,i) \ge g_idis(u,fai)gfai\operatorname{dis}(u, fa_i) \le g_{fa_i}

于是,问题就转化为:对于一个 uu,求有多少个 ii 满足上述条件,求解的时间复杂度为 O(n2)\mathcal{O}(n^2)

考虑优化。

degideg_i 表示 ii 的度数,sizisiz_i 表示 ii 的子树大小。

我们发现 vsub(u)degv=2×sizu1\sum\limits_{v\in sub(u)}deg_v=2 \times siz_u - 1,即 1=vsub(u)(2degv)1 = \sum\limits_{v \in sub(u)}(2 - deg_v)

而一个子树的贡献只有 11,所以我们可以将式子转化为:ansu=i=1n[dis(u,i)gi](2degi)ans_u=\sum\limits_{i=1}^{n} [\operatorname{dis}(u,i)\ge g_i](2-deg_i),此时就已经自动选满足的且深度最小的了。

考虑再次优化:

对于这种无根树,我们可以想到用点分治,以当前分治中心为根,维护有多少个 ii 满足 gidepu+depig_i \leq dep_u+dep_i,即 depugidepidep_u \geq g_i-dep_i,树状数组维护一下即可,注意链重合的情况。

时间复杂度为 O(nlog2n)\mathcal O(n \log^2 n)

#include <bits/stdc++.h>

using namespace std;

const int _ = 2e5 + 10;

inline int read()
{
	int x = 0, f = 1;
	char c = getchar();
	while (c < '0' || c > '9')
	{
		if (c == '-')
			f = -1;
		c = getchar();
	}
	while (c >= '0' && c <= '9')
		x = x * 10 + (c - '0'), c = getchar();
	return x * f;
}

int n;

int tot, head[_], to[_ << 1], nxt[_ << 1];

bool vis[_];

int g[_], siz[_], mx[_], deg[_], d[_], p[_], ans[_], rt, sum;

int cnt, o[_];

int c[_];

void update(int x, int val)
{
	for(; x <= 2 * n; x += x & -x)
		c[x] += val;
}

int query(int x)
{
	int res = 0;
	for(; x; x -= x & -x)
		res += c[x];
	return res;
}

void add(int u, int v)
{
	to[++tot] = v;
	nxt[tot] = head[u];
	head[u] = tot;
}

void dfs1(int u, int fa)
{
	siz[u] = 1;
	for(int i = head[u]; i; i = nxt[i])
	{
		int v = to[i];
		if(v == fa)
			continue;
		dfs1(v, u);
		g[u] = min(g[u], g[v] + 1);
	}
}

void dfs2(int u, int fa)
{
	for(int i = head[u]; i; i = nxt[i])
	{
		int v = to[i];
		if(v == fa)
			continue;
		g[v] = min(g[v], g[u] + 1);
		dfs2(v, u);
	}
}

void get_rt(int u, int fa)
{
	siz[u] = 1, mx[u] = 0;
	for(int i = head[u]; i; i = nxt[i])
	{
		int v = to[i];
		if(vis[v] || v == fa)
			continue;
		get_rt(v, u);
		siz[u] += siz[v];
		mx[u] = max(mx[u], siz[v]);
	}
	mx[u] = max(mx[u], sum - siz[u]);
	if(mx[u] < mx[rt])
		rt = u;
}


void get_dis(int u, int fa)
{
	o[++cnt] = u;
	for(int i = head[u]; i; i = nxt[i])
	{
		int v = to[i];
		if(v == fa || vis[v])
			continue;
		d[v] = d[u] + 1;
		get_dis(v, u);
	}
}

void calc(int u, int len, int mul)
{
	d[u] = len, cnt = 0;
	get_dis(u, 0);
	for(int i = 1; i <= cnt; ++i)
		update(g[o[i]] - d[o[i]] + n, 2 - deg[o[i]]);
	for(int i = 1; i <= cnt; ++i)
	{
		update(g[o[i]] - d[o[i]] + n, -(2 - deg[o[i]]));
		ans[o[i]] += query(d[o[i]] + n) * mul;
		update(g[o[i]] - d[o[i]] + n, 2 - deg[o[i]]);
	}
	for(int i = 1; i <= cnt; ++i)
		update(g[o[i]] - d[o[i]] + n, -(2 - deg[o[i]]));
}

void dfz(int u)
{
	vis[u] = 1;
	calc(u, 0, 1);
	for(int i = head[u]; i; i = nxt[i])
	{
		int v = to[i];
		if(vis[v])
			continue;
		calc(v, 1, -1);
		rt = 0, sum = siz[v];
		get_rt(v, u), dfz(rt);
	}
}

signed main()
{
	memset(g, 0x3f, sizeof g);
	n = read();
	int a, b;
	for(int i = 1; i < n; ++i)
	{
		a = read(), b = read();
		add(a, b), add(b, a);
		deg[a]++, deg[b]++;
	}
	for(int i = 1; i <= n; ++i)
		if(deg[i] == 1) g[i] = 0;
	dfs1(1, 0), dfs2(1, 0);
	rt = 0, sum = n, mx[0] = 2e9;
	get_rt(1, 0), dfz(rt);
	for(int i = 1; i <= n; ++i)
		printf("%d\n", deg[i] == 1 ? 1 : ans[i]);
	return 0;
}
posted @ 2022-03-19 11:28  蒟蒻orz  阅读(5)  评论(0编辑  收藏  举报  来源