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
2∗m−x个,可以证明跨过这一条边匹配是优的,然后每一条边的贡献就是
min
(
x
,
2
∗
m
−
x
)
\min(x, 2*m-x)
min(x,2∗m−x)
即答案就是
∑
min
(
s
i
z
e
[
v
]
,
2
∗
m
−
s
i
z
e
[
v
]
)
\sum\min(size[v],2*m-size[v])
∑min(size[v],2∗m−size[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……