【BZOJ】3714: [PA2014]Kuglarz

时间:2023-03-08 17:47:12
【BZOJ】3714: [PA2014]Kuglarz

题意

\(n(1 \le n \le 2000)\)个数每个数是\(0\)或\(1\),现在可以花费\(c_{i, j}\)知道\([i, j]\)的奇偶性,问将所有数都找出来的最小花费。

分析

如果知道了所有的前缀和,那么我们就知道了所有数。

对于区间\([i, j]\),那么如果知道了\(sum[i-1]\),那么就知道了\(sum[j]\),连边。反之亦然。

最终其实我们就是将前缀和\(0\)到\(n\)都放到一个集合里,由于知道了前缀和\(0\)=0,所以就知道了所有数。

题解

所以问题转化为找出一种方案使得所有前缀和在同一集合内的最小解。

最小生成树= =。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=2005;
int n, s[N], p[N], tot;
struct E {
int x, y, w;
}e[N*N/2];
inline bool cmp(const E &a, const E &b) {
return a.w<b.w;
}
inline int find(const int x) {
return x==p[x]?x:p[x]=find(p[x]);
}
inline int getint() {
int x=0;
char c=getchar();
for(; c<'0'||c>'9'; c=getchar());
for(; c>='0'&&c<='9'; c=getchar()) {
x=x*10+c-'0';
}
return x;
}
int main() {
scanf("%d", &n);
for(int i=1; i<=n; ++i) {
for(int j=i; j<=n; ++j) {
e[tot++]=(E){i-1, j, getint()};
}
p[i]=i;
}
ll ans=0;
sort(e, e+tot, cmp);
for(int i=0; i<tot; ++i) {
int fx=find(e[i].x), fy=find(e[i].y);
if(fx!=fy) {
if(s[fx]>s[fy]) {
swap(fx, fy);
}
ans+=e[i].w;
p[fx]=fy;
s[fy]+=s[fx]==s[fy]?1:0;
}
}
printf("%lld\n", ans);
return 0;
}