好妙的一道最小生成树的题啊!
记 表示前 杯子的球的奇偶性的异或和,则区间 内的所有球的奇偶性的异或和即为 异或 的值。
把对区间 的询问看着从 连一条边到 ,我们需要保证我们的询问没有环,而且需要 次询问总花费最小。
想到了什么?对,没错,最小生成树,这就是最小生成树的典型应用!!!
理论上来说,因为输入是完全图,我们应该用Prim
算法来做,但是因为笔者个人的习惯,代码中用了Kruskal
算法。实测,效率在洛谷上还可以。
const int N=1e3+100;
struct union_set{
int f[N],s[N];
void init(int n){
for(int i=0;i<=n;i++){
f[i]=i;s[i]=1;
}
}
int getf(int x){
if (f[x]==x) return x;
return f[x]=getf(f[x]);
}
void merge(int x,int y){
int a=getf(x),b=getf(y);
if (s[a]<s[b]) swap(a,b);
if (a==b) return;//Remember it!!!
f[b]=a;s[a]+=s[b];
}
bool query(int a,int b){
return getf(a)==getf(b);
}
}F;int n,tot;long long ans;
struct node{
int u,v,cost;
bool operator < (node c) const{
return cost<c.cost;
}
}e[N*(N+1)>>1];
int main(){
freopen("t1.in","r",stdin);
n=read();ans=0ll;tot=0;
for(int i=1;i<=n;i++)
for(int j=i;j<=n;j++)//注意是从i枚举到n
e[++tot]=(node){i-1,j,read()};//注意从i-1连边到j
sort(e+1,e+tot+1);F.init(n);
for(int i=1,t=0;i<=tot;i++)
if (!F.query(e[i].u,e[i].v)){
F.merge(e[i].u,e[i].v);
ans+=e[i].cost;t++;
if (t==n) break;
}
cout<<ans;
return 0;
}
- 代码中的
read()
是快读函数,这里就不给出了。 union_set
结构体即并查集,这是一个好习惯,即把数据结构封装到一个结构体中!!!