DD 摆磁铁(计蒜客信息学8月普及组模拟赛)

DD 摆磁铁
这道题来自 计蒜客信息学8月普及组模拟赛

普及组!!

把我一个TG选手看懵了
看来我要回去打普及了

题目大意

给出一个n个节点的树,要把树上给定的2*m个节点两两配对,两个节点配对的产生的价值就是这两个点的距离,求最大的距离和。
即使 ∑ i = 1 m d i s p a i r \sum_{i=1}^{m}dis_{pair} i=1mdispair 最大化

题解

首先这题肯定是不能DP的,因为是多点配对,所以应该要往贪心那方面想。
点配对的贪心也并不是很好想,那我们可以把思维发散一些
考虑每一条边的贡献
假设一条边的两边分别有 x x x个和 2 ∗ m − x 2*m - x 2mx个,可以证明跨过这一条边匹配是优的,然后每一条边的贡献就是
min ⁡ ( x , 2 ∗ m − x ) \min(x, 2*m-x) min(x,2mx)
即答案就是 ∑ min ⁡ ( s i z e [ v ] , 2 ∗ m − s i z e [ v ] ) \sum\min(size[v],2*m-size[v]) min(size[v],2msize[v])
然后就没了

#include<bits/stdc++.h>
#define N 400005
using namespace std;
struct edge{
	int v, nxt;
}e[N];
int p[N], eid;
void init(){
	memset(p, -1, sizeof p);
	eid = 0;
}
void insert(int u, int v){
	e[eid].v = v;
	e[eid].nxt = p[u];
	p[u] = eid ++;
}
int size[N], n, m;
long long ans;
void dfs(int u, int fa){
	for(int i = p[u]; i + 1; i = e[i].nxt){
		int v = e[i].v;
		if(v == fa) continue;
		dfs(v, u);
		size[u] += size[v];
		ans += min(2 * m - size[v], size[v]);//累加(u,v)这条边的贡献
	}
}
int main(){
	init();
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= 2 * m; i ++){
		int x = 0;
		scanf("%d", &x);
		size[x] ++;
	}
	for(int i = 1; i < n; i ++){
		int u, v;
		scanf("%d%d", &u, &v);
		insert(u, v);
		insert(v, u);
	}
	dfs(1, 1);
	printf("%lld", ans);
	return 0;
} 

总结

以后做题思维还是要发散一些,一个思路感觉行不通要立马换,联赛的题不会太难,主要考的还是思维

智力康复ing……

posted @ 2019-08-10 19:50  lahlah  阅读(15)  评论(0编辑  收藏  举报