一名苦逼的OIer,想成为ACMer

Iowa_Battleship

BZOJ1060或洛谷1131 [ZJOI2007]时态同步

BZOJ原题链接

洛谷原题链接

看上去就觉得是一道树形\(\mathtt{DP}\),不过到头来我发现我写了一个贪心。。

显然对越靠近根(记为\(r\))的边进行加权贡献越大,且同步的时间显然是从根到各个叶子节点的时间中的最大值。
于是先求出同步的时间,记\(f[x]\)表示从根到以\(x\)节点为根的子树中的各个叶子节点的时间中的最大值。
显然\(f[x] = \max\limits^{y\in son_x} f[y]\),而\(f[x]\)初始化为它父亲到它的边的边权\(+f[\text{父亲}]\)
这个只需要\(dfs\)一遍即可求出。
然后从根开始往叶子节点贪心的去加边权,同样使用\(dfs\)来完成。
对于一个节点\(x\),若\(f[x]<f[r]\),则表示从根到以它为根的子树中的各个叶子节点的时间中的最大值都未能同步,那么就将\(x\)到它父亲的边权加上\(k = f[r] - f[x]\),使得最远的那个叶子节点的时间同步。
因为给到父亲的边加了边权,那么后面的点都受到影响,需将\(k\)累加到\(x\)的子节点的\(f\)值中,注意一条路径上的\(k\)是会不断累加的。
然后就这样搜下去,最后的答案就是每次给边加权的累计。
注意开\(\mathtt{long\ long}\)

#include<cstdio>
using namespace std;
typedef long long ll;
const int N = 5e5 + 10;
const int M = N << 1;
int fi[N], da[M], di[M], ne[M], l, ro;
ll f[N], s;
inline int re()
{
	int x = 0;
	char c = getchar();
	bool p = 0;
	for (; c < '0' || c > '9'; c = getchar())
		p |= c == '-';
	for (; c >= '0' && c <= '9'; c = getchar())
		x = x * 10 + c - '0';
	return p ? -x : x;
}
inline ll maxn(ll x, ll y) { return x > y ? x : y; }
inline void add(int x, int y, int z)
{
	di[++l] = y; da[l] = z; ne[l] = fi[x]; fi[x] = l;
	di[++l] = x; da[l] = z; ne[l] = fi[y]; fi[y] = l;
}
void dfs_1(int x, int fa)
{
	int i, y; ll ma = f[x];
	for (i = fi[x]; i; i = ne[i])
		if ((y = di[i]) ^ fa)
		{
			f[y] = da[i] + f[x];
			dfs_1(y, x);
			ma = maxn(ma, f[y]);
		}
	f[x] = ma;
}
void dfs_2(int x, int fa, ll k)
{
	int i, y;
	s += f[ro] - f[x]; k += f[ro] - f[x];
	for (i = fi[x]; i; i = ne[i])
		if ((y = di[i]) ^ fa)
			f[y] += k, dfs_2(y, x, k);
}
int main()
{
	int i, n, x, y, z;
	n = re(); ro = re();
	for (i = 1; i < n; i++)
	{
		x = re(); y = re(); z = re();
		add(x, y, z);
	}
	dfs_1(ro, 0); dfs_2(ro, 0, 0);
	printf("%lld", s);
	return 0;
}

posted on 2019-06-19 21:00  Iowa_Battleship  阅读(108)  评论(0编辑  收藏  举报

导航