Time Limit: 2000/1000MS (Java/Others)
Memory Limit: 65535/65535KB (Java/Others)
题目描述
有一个含 n 项的数列 A1 、A2 、… 、An。
虽然不知道每一项的具体值,但我们知道每一项的值要么为 0,要么为1。
而且我们可以花 Ci,j的费用询问数列中第 i 项到第 j项的异或和。
第 i项到第 j 项的异或和为 Ai ⊕ Ai+1 ⊕ … ⊕ Aj。
其中 0 ⊕ 0 = 0 、0 ⊕ 1 = 1 、1 ⊕ 0 = 1 、1 ⊕ 1 = 0。
那么我们至少要花多少费用才能知道数列中每一项的具体值呢?
Input
第一行一个整数 n,表示数列的项数。
接下来 n行中,第 i 行有 n+1−i 个整数,即 Ci,i 、Ci,i+1 、… 、Ci,n。
1≤n≤1000,1≤Ci,j≤1000000
。
Output
输出一个整数,即要花的最小费用。
Sample Input1
2
1 2
2
Sample Output1
3
Sample Input2
3
1 2 3
2 1
3
Sample Output2
4
Source
2017 UESTC Training for Graph Theory
题解:
我们需要知道每一个位置的具体数值,那么很显然的一个结论,我们至少要查询n次。那么这n次要如何分配呢?有一个很奇异的想法,Ci,j其实看作每两个点之间的连边的边权,那么好像其实最小生成树就是答案了。但是有一个问题:至少要有一个自环要被使用,而且把一棵生成树拆成多个生成树说不定更优23333。
考虑重新建图,将点i拆成i和i+1,Ci,j的连边是i<->j+1,这样可以保证每个自环都可以出现在图上并且都有机会被用到,更好的是这样还保持了图的连通性。那么这时候再跑一次最小生成树就OK啦。
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
#include<cstdlib>
#include<queue>
#define INF 1999122700
#define pa pair<int,int>
#define LiangJiaJun main
#define INF 1999122700
using namespace std;
int mp[1004][1004],mst[1004];
priority_queue<pa,vector<pa>,greater<pa> >q;
int n,mint=INF;
bool vis[1004];
int prim(){
int ans=0;
for(int i=1;i<=n+1;i++)mst[i]=INF;
mst[1]=0;
memset(vis,0,sizeof(vis));
q.push(make_pair(mst[1],1));
while(!q.empty()){
int x=q.top().second;q.pop();
if(vis[x])continue;vis[x]=1;
ans+=mst[x];
for(int i=1;i<=n+1;i++){
if( mst[i]>mp[x][i]){
mst[i]=mp[x][i];
q.push(make_pair(mst[i],i));
}
}
}
return ans;
}
int LiangJiaJun(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
for(int j=i;j<=n;j++){
scanf("%d",&mp[i][j+1]);
mp[j+1][i]=mp[i][j+1];
}
}
int ret = prim();
printf("%d\n",ret);
return 0;
}