ssoj2468设置

【题意】给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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值