[PA2014]Kuglarz

  我们用一个数列 \(\{a_n\}\) 来表示这些杯子,\(a_n=1\) 表示第 \(n\) 个杯子下藏有小球,\(a_n=0\) 表示没有。那么第 \([l,r]\) 个杯子底下藏有球的总数的奇偶性就可以用这一段上的异或和 \((a_l \text{xor} a_{l+1} \cdots \text{xor} a_r)\) 来表示。设 \(\{a_n\}\) 的异或前缀和为 \(\{b_n\}(b_n=a_1 \text{xor} a_2 \text{xor} a_3 \cdots \text{xor} a_n)\)

花费 \(c_{i,j}\) 元,魔术师就会告诉你杯子 \(i,i+1,…,j\) 底下藏有球的总数的奇偶性。

  也即每询问一次 \([l, r]\),我们可以得到 \(b_r \text{xor} b_{l-1}\) 的结果。
  如果我们求出了 \(\{b_n\}\),便可以很快求出 \(\{a_n\}\),“保证猜出哪些杯子底下藏着球”。显然 \(b_0 = 0\),而 \(b_1∼b_n\) 是未知的。我们每询问一次,都可以得到一个方程。要解出这 \(n\) 个未知数,就需要 \(n\) 个方程联立。也就是说我们总共需要询问 \(n\) 次。
  将 \(\{b_n\}\) 看作一系列的点,将询问看作连接边 \((l-1,r)\),那么我们所要求的就变成了用 \(n\) 条边连通 \(0∼n\)\(n+1\) 个点,使得边权总和最小,即求最小生成树。该题给出的是稠密图,适合使用 prim 进行求解,复杂度为 \(O(n^2)\)
  代码如下。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN=2010;
int n,t;
int e[MAXN][MAXN],dist[MAXN];
ll ans;
bool book[MAXN];

void prim(){
    memset(dist,0x3f,sizeof(dist));
    dist[0]=0;
    for(int i=0;i<n;++i){
        int x=-1;
        for(int j=0;j<=n;++j){
            if(book[j]) continue;
            if(x==-1 || dist[j]<dist[x]) x=j;
        }
        book[x]=true;
        for(int y=0;y<=n;++y){
            if(!book[y]) dist[y]=min(dist[y],e[x][y]);
        }
    }
}

int main(){
    scanf("%d",&n);
    memset(e,0x3f,sizeof(e));
    for(int i=0;i<=n;++i) e[i][i]=0;
    for(int l=1;l<=n;++l){
        for(int r=l;r<=n;++r){
            scanf("%d",&t);
            e[l-1][r]=e[r][l-1]=t;
        }
    }
    prim();
    for(int i=1;i<=n;++i) ans+=dist[i];
    printf("%lld",ans);
    return 0;
}
posted @ 2022-08-15 18:35  东方澂  阅读(19)  评论(0编辑  收藏  举报