P5994 [PA2014]Kuglarz(思维 最小生成树)
P5994 [PA2014]Kuglarz(思维 最小生成树)
题意:
你可以询问\([l, r]\)区间的杯子下球的总数的奇偶性,花费是\(cost_{i,j} (i \le j)\)。若想要知道每个杯子下有无球,求最小花费是多少。杯子的数量为\(2e3\)。
思路:
很容易知道,若想要知道第\(i\)个杯子下面有没有球,有两种方式。1.直接询问\([i, i]\)。2.询问\([i, j]+[i + 1, j]或者[j, i]+[j,i-1]\)。
那就能想到一个很暴力的方法,也就是区间DP(n ^ 3)。不过复杂度过高,不可写。
现在假设一个超级源点0,[0, 0]是没有球的。如果我们知道所有的\([0, i](1 \le i \le n)\),就可以知道每个位置有无小球。由于上面的结论, 我们可以知道,如果我们知道了\([0, i - 1]和[i, j]\)的奇偶性,就可以知道\([0, j]\)的奇偶性。同理,知道\([0,j]和[i, j]\)就可以知道\([0, i-1]\)。
所以,对于区间\([a, b]\),我们可以将\([0, a-1]\)和\([0,b]\)之间连一条权值为\(cost_{a,b}\)的边,表示我知道了这两点中的一个,就可以花费\(cost_{a,b}\)得到另一个。如果我们想要知道所有的杯子下有无球,那就需要知道\([0, i](i \le n)\),也就是整个图联通。
我们按上述做法建图,跑一个裸的最小生成树就可以了。
实现:
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2005, M = N * N / 2;
int n;
int cost[N][N];
int res;
struct node {
int l, r, cost;
bool operator < (const node &a) const {
return cost < a.cost;
}
} edges[M];
int idx = 0;
int fa[N];
int fd(int x)
{
if(fa[x] != x) fa[x] = fd(fa[x]);
return fa[x];
}
void kruskal()
{
int cnt = 0;
res = 0;
for(int i = 1; i <= idx; i ++)
{
auto [a, b, w] = edges[i];
int ta = fd(a), tb = fd(b);
if(ta == tb) continue;
fa[tb] = ta;
res += w, cnt ++;
if(cnt == n) break;
}
}
signed main()
{
scanf("%lld", &n);
for(int i = 1; i <= n; i ++) fa[i] = i;
for(int i = 1; i <= n; i ++)
for(int j = i; j <= n; j ++)
{
scanf("%lld", &cost[i][j]);
edges[++ idx] = {i - 1, j, cost[i][j]};
}
sort(edges + 1, edges + idx + 1);
kruskal();
printf("%lld\n", res);
}