BZOJ 3714 Kuglarz

BZOJ 3714 Kuglarz

非常玄学的一道题....到现在还没理解为什么是最小生成树的题...
这里就从另一个角度解析题目,首先题目中每个杯子中是否藏有球可以视为0/1,而我们询问的一个区间的奇偶可以看做是若干个变量相异或的和,而每个变量的意义就是该杯子下是否藏有求0/1.那么我们的目的是想知道这n个变量的值,根据线性代数的一些知识,我们知道解出n个变量的唯一解的冲要条件是有n个本质上不同的方程(其实这也可以通过手玩样例来得出)。那我们怎么来选出这n个方程,并且维护他们是否已经被已有的方程给生成了。我们可以举个例子就能简单的判断,第一个方程:l-mid,第二个方程mid+1,r,第三个方程,l,r。显然第三个方程可以被前两个方程相加得到。维护关系的话我们很容易想到的就在边界处两边,可直接在l-mid,和mid+1-r连边,无法看出l和r的关系。同时我们发现只有相邻的时候才会合并,那我们将l-mid+1连边,mid+1-r连边即可。这样的话l-r就联通,说明l-r这个区间就不行了。那我们知道最后n个方程,即n个边最后的结果就是1到n+1的一个最小生成树。

点击查看代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2010;
int n,f[N],num;
struct wy{int x,y,v;}a[N*N*2]; 
inline int getf(int x){return f[x]==x?x:f[x]=getf(f[x]);}
inline bool cmp(wy a,wy b){return a.v<b.v;}
inline void kruskal()
{
    sort(a+1,a+num+1,cmp);
    for(int i=1;i<=n+1;++i) f[i]=i;
    int cnt=0;
    ll ans=0;
    for(int i=1;i<=num;++i)
    {
        int t1=getf(a[i].x),t2=getf(a[i].y);
        if(t1!=t2)
        {
            f[t1]=t2;
            ans+=a[i].v;
            if(++cnt==n) break;
        }
    }
    printf("%lld",ans);
}
int main()
{
//    freopen("1.in","r",stdin);
    scanf("%d",&n);
    for(int i=1;i<=n;++i)
        for(int j=i;j<=n;++j)
        {
            int x;scanf("%d",&x);
            a[++num].x=i;a[num].y=j+1;
            a[num].v=x;
        }
    kruskal();    
    return 0;
} 
posted @ 2022-02-04 17:15  逆天峰  阅读(17)  评论(0编辑  收藏  举报
作者:逆天峰
出处:https://www.cnblogs.com/gcfer//