您的位置:首页 > 其它

ssoj2468设置

2015-10-31 22:04 225 查看
【题意】给n行m列数,每行取一个数组成新数,求新数中前k个最小值的异或值。

【题解】%%wwx。

这其实是一个用堆求k优解的一般思路。

先对于每个i,将元件i的a[i][1]~a[i][m]从小到大排序,再将所有元件按照其(第2~第m种设置与第1种设置的差值)多关键字从小到大排序(共m-1个关键字)。

现在开始,我们将排在第i位的元件称为元件i,其第j小的设置称为元件i的设置j。

那么我们知道,最小的方案肯定是所有元件都设置为1。由于其有一些特殊,我们先抛开这个方案。(实际上不抛开也是可行的)

我们知道,次小的方案是(2,1,1,1…),我们以此为起点s,由较优方案扩展较劣方案,对于每一个方案,我们记录其最后被扩展的位置(对于s,这个位置为1)。在已经得到前t优的方案时,当前所有方案中还未扩展的最好的方案x(其最后扩展位置为i),就是第t+1优。

从方案x,我们可以扩展出几个较劣解:

1、 i<n:将i+1号元件设置为2(扩展位置为i+1)

2、 x的第i个元件设置为2且i<n:将i号元件设置为1,i+1号元件设置为2(扩展位置为i+1)

3、 x的第i个元件设置不为m:将i号元件设置增加1(扩展位置为i)

由此,每个解都可由唯一的优于它的解扩展得来。

用堆维护当前所有解即可。

【代码】

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
#include <queue>
#define ll long long
using namespace std;
const int maxn=300005;
int n,m,k;
ll ans=0,b[maxn];
struct data{
ll *v;
bool operator<(const data&fn)const{
for(int i=1;i<m;++i)if(v[i]!=fn.v[i])return v[i]<fn.v[i];//按字典序排序
return 0;
}
}a[maxn];
struct da{
ll w;
int h,l;
da(ll w,int h,int l):w(w),h(h),l(l){};
bool operator >(const da&fn)const{
return w>fn.w;
}
};
inline int get(){
char c;while(!isdigit(c=getchar()));
int v=c-48;while(isdigit(c=getchar()))v=v*10+c-48;
return v;
}
priority_queue< da,vector<da>,greater<da> >q;
int main(){
n=get();m=get();k=get();
for(int i=1;i<=n;++i){
a[i].v=b+(i-1)*m;//可以根据m来分配内存
for(int j=0;j<m;++j)a[i].v[j]=get();
sort(a[i].v,a[i].v+m);ans+=a[i].v[0];
for(int j=m-1;j>0;--j)a[i].v[j]-=a[i].v[j-1];//差量
}
sort(a+1,a+1+n);
q.push(da(ans+a[1].v[1],1,1));//大根堆维护
while(--k){
da x=q.top();q.pop();
ans^=x.w;
if(x.l<m-1){   //三种转移
q.push(da(x.w+a[x.h].v[x.l+1],x.h,x.l+1));
}
if(x.l>0&&x.h<n){
q.push(da(x.w+a[x.h+1].v[1],x.h+1,1));
}
if(x.l==1&&x.h<n){
q.push(da(x.w-a[x.h].v[x.l]+a[x.h+1].v[1],x.h+1,1));
}
}
printf("%lld\n",ans);
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: