每次询问所获得的可以看做是两个前缀和的异或。我们只要知道任意前缀和的异或就可以得到答案了。并且显然地,如果知道了a和b的异或及a和c的异或,也就知道了b和c的异或。所以一次询问可以看做是在两点间连边,所要求的东西就是最小生成树了。
#include<iostream> #include<cstdio> #include<cmath> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; int read() { int x=0,f=1;char c=getchar(); while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();} while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar(); return x*f; } #define N 2010 int n,fa[N]; long long ans=0; struct data { int x,y,z; bool operator <(const data&a) const { return z<a.z; } }edge[N*N]; int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);} int main() { #ifndef ONLINE_JUDGE freopen("bzoj3714.in","r",stdin); freopen("bzoj3714.out","w",stdout); const char LL[]="%I64d\n"; #else const char LL[]="%lld\n"; #endif n=read();int t=0; for (int i=1;i<=n;i++) for (int j=i;j<=n;j++) t++,edge[t].x=i-1,edge[t].y=j,edge[t].z=read(); sort(edge+1,edge+t+1); for (int i=0;i<=n;i++) fa[i]=i; for (int i=1;i<=t;i++) if (find(edge[i].x)!=find(edge[i].y)) ans+=edge[i].z,fa[find(edge[i].x)]=find(edge[i].y); cout<<ans; return 0; }