链接
http://www.lydsy.com/JudgeOnline/problem.php?id=3714
题解
这题思路很神奇。
要知道每一个数,可以从前缀和的角度考虑。
如果知道了所有的前缀和,就一定能知道所有的数了,而且这两个问题一定是完全等价的,费用肯定也是一样的。
异或是可以前缀和的,考虑一个操作
cij
,显然就是告诉了你
si−1
和
sj
的大小关系。有了这条关系之后,以后只要知道其中一个值,另一个就出来了,因此我们在图中建一条边
eij
,权值就是
cij
。
显然一开始唯一知道的就是
s0
,那你要做的工作就是花最小的费用使所有点和
0
这个点连通。
代码
//最小生成树
#include <cstdio>
#include <algorithm>
#define maxn 2017
#define ll long long
using namespace std;
int u[maxn*maxn], v[maxn*maxn], w[maxn*maxn], tot, f[maxn], num[maxn*maxn], N;
ll cost;
int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
void merge(int a, int b){f[find(a)]=find(b);}
bool cmp(int a, int b){return w[a]<w[b];}
void kruskal()
{
int i;
for(i=1;i<=tot;i++)num[i]=i;
for(i=1;i<=N;i++)f[i]=i;
sort(num+1,num+tot+1,cmp);
for(i=1;i<=tot;i++)
if(find(u[num[i]])^find(v[num[i]]))merge(u[num[i]],v[num[i]]),cost+=w[num[i]];
}
int main()
{
int x, i, j;
scanf("%d",&N);
for(i=1;i<=N;i++)for(j=i;j<=N;j++)u[++tot]=i-1, v[tot]=j, scanf("%d",w+tot);
kruskal();
printf("%lld",cost);
return 0;
}