[题解] [bzoj4886] 叠塔游戏

题面

题解

可以将题目转化为在满足宽度互不相同的情况下最大化长度之和

将一个矩形的\(a\)\(b\)连一条边, 定义边的出点和入点分别代表选择该点的值作为宽度一次, 选择该点的值作为高度

所以我们要做的就是将\(a\)\(b\)这一条边定向

除此之外, 还要注意到每个点至多被作为出点一次, 这也满足了我们将题目转化后的条件 -- 宽度互不相同

分析一个联通块, 设其中点有\(n\)个, 边有\(m\)条, 可以想到\(m > n\)的情况是不成立的, 这样至少会有一个点被作为出点多次

所以只剩下两种情况\(m = n\)\(m = n - 1\), 因为每个点都要选, 所以\(m < n - 1\)是不合法的(最少有一个点不作为宽度和长度, 但所有点都要被选)

我们分开讨论

第一种

可以发现\(m = n\)是一棵基环树

由于每个点被作为高度的次数就是其被作为出点的次数

而每个点被作为出点的次数就是它的总度数减去其作为入点的次数

大家画一个图可以知道这个图是被定向的了

环内的点有顺时针逆时针两种方式, 不过最后的贡献都一样

而环外的点不论环内方向如何都是固定的方向

所以基环树中每一个点入点的次数都为1

可以得到\(m = n\)的情况总贡献是

\[\sum_{i\in S}w_i*(deg_i-1) \]

第二种

可以发现\(m = n - 1\)是一棵树

大家可以发现每个点被作为入点一次有点像树中每个点有且仅有一个父亲

所以这棵树也是被定向了的, 方向是从根节点向叶节点

注意到根节点是没有被作为入点过的

\(m = n - 1\)的总贡献是

\[w_{root}\times deg_{root}+\sum_{i\in S\And i!=root}w_i \times (deg_i - 1) \]

可以发现选择权值最大的点作为根节点的总贡献最大

最后判一下有几个联通块, 记一下联通块中是否有环并且联通块中的最大值即可

Code(代码还是挺简单的)

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#define N 500005
using namespace std;

int n, a[N], b[N], c[N << 1], cnt, fa[N << 1], deg[N << 1], mx[N << 1];
long long ans; 
bool is[N << 1]; 

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

int find(int x) { return fa[x] == x ? x : fa[x] = find(fa[x]); }

int main()
{
	n = read();
	for(int i = 1; i <= n; i++)bzoj
	{
		c[++cnt] = a[i] = read();
		c[++cnt] = b[i] = read(); 
	}
	sort(c + 1, c + cnt + 1); cnt = unique(c + 1, c + cnt + 1) - c - 1;
	for(int i = 1; i <= n; i++)
	{
		a[i] = lower_bound(c + 1, c + cnt + 1, a[i]) - c;
		b[i] = lower_bound(c + 1, c + cnt + 1, b[i]) - c; 
	}
	for(int i = 1; i <= cnt; i++) fa[i] = i, mx[i] = i; 
	for(int i = 1; i <= n; i++)
	{
		deg[a[i]]++; deg[b[i]]++;
		int u = find(a[i]), v = find(b[i]); 
		if(u == v) is[v] = 1; 
		else fa[u] = v, is[v] |= is[u], mx[v] = max(mx[v], mx[u]); 
	}
	for(int i = 1; i <= cnt; i++)
	{
		ans += 1ll * (deg[i] - 1) * c[i];
		if(i == fa[i] && !is[i]) ans += 1ll * c[mx[i]]; 
	}
	printf("%lld\n", ans); 
	return 0; 
} 
posted @ 2019-07-13 22:27  ztlztl  阅读(195)  评论(0编辑  收藏  举报