Loading

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);
}
posted @ 2022-10-11 09:18  DM11  阅读(30)  评论(0编辑  收藏  举报