BZOJ4345: [POI2016]Korale

77 篇文章 0 订阅
41 篇文章 0 订阅

感觉..这道题好像和多校某题很像,当时那题我没做结果现在又不会做了..

现将珠子按价值排序
我们用一个二元组(x,i)代表当前选的珠子和为x,最后一个珠子是第i个
它可以转移到(x-a[i]+a[i+1],i+1),(x+a[i+1],i+1),且这种转移方式能不重不漏的覆盖所有的情况
又因为每次转移,新的状态和一定不比原来小,所以我们从(a[1],1)开始转移k-1次一定能得到第k小的价值,复杂度klogk

然后考虑寻找方案
我们在转移中记录第k小的价值有多少个,设为m个
又因为我们知道第k小的值x是多大,在寻找方案的时候,用原数组,假设当前方案最后一个数是第L-1个,x-已经取了的值=limit,那么我们在线段树上二分,在[L,n]里找不超过limit的数一个一个取出来,
因为我们每次取一个不超过限制的数,都代表拓展到了一个不超过第k小的状态,而且每次拓展的状态互不相同,所以最多拓展k次,每次在线段树上耗费logn,复杂度klogn

code:

#include<set>
#include<map>
#include<deque>
#include<queue>
#include<stack>
#include<cmath>
#include<ctime>
#include<bitset>
#include<string>
#include<vector>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<climits>
#include<complex>
#include<iostream>
#include<algorithm>
#define ll long long
#define inf 1e9
using namespace std;

const int maxn = 1100000;

int n,m;
struct node
{
    ll x;
    int i;
};
inline bool operator <(const node x,const node y){return x.x>y.x;}
priority_queue<node>q;

int a[maxn],b[maxn];
int seg[maxn<<2];
void build(const int x,const int l,const int r)
{
    if(l==r) { seg[x]=a[l]; return; }
    int mid=l+r>>1,lc=x<<1,rc=lc|1;
    build(lc,l,mid); build(rc,mid+1,r);
    seg[x]=min(seg[lc],seg[rc]);
}
int lx; ll u;
int search(const int x,const int l,const int r)
{
    if(l==r) return l;
    int mid=l+r>>1,lc=x<<1,rc=lc|1;
    if(seg[lc]<=u) return search(lc,l,mid);
    else return search(rc,mid+1,r);
}
int query(const int x,const int l,const int r)
{
    if(r<lx||seg[x]>u) return -1;
    if(l>=lx) return search(x,l,r);
    int mid=l+r>>1,lc=x<<1,rc=lc|1;
    int rl=query(lc,l,mid);
    if(rl!=-1) return rl;
    return query(rc,mid+1,r);
}

int t[maxn],tp,now,sn;
void dfs(const ll limit,int L)
{
    if(!limit) 
    {
        now++; if(now==sn) return;
        return;
    }
    while(1)
    {
        lx=L,u=limit; if(lx>n) return;
        int tmp=query(1,1,n);
        if(tmp==-1) return;
        t[++tp]=tmp,L=tmp+1;
        dfs(limit-b[tmp],L);
        if(now==sn) return;
        tp--;
    }
}

int main()
{
    scanf("%d%d",&n,&m);
    if(m==1) { printf("0\n");return 0; } m--;
    for(int i=1;i<=n;i++) scanf("%d",&a[i]),b[i]=a[i];
    build(1,1,n);
    sort(a+1,a+n+1);

    ll s=-1;
    q.push((node){a[1],1});
    for(int i=1;i<=m;i++)
    {
        const node tmp=q.top(); q.pop();
        if(tmp.x!=s) s=tmp.x,sn=1;
        else sn++;
        if(tmp.i<n) q.push((node){tmp.x-a[tmp.i]+a[tmp.i+1],tmp.i+1}),q.push((node){tmp.x+a[tmp.i+1],tmp.i+1});
    }
    tp=0,dfs(s,1);
    printf("%lld\n",s);
    for(int i=1;i<=tp;i++) printf("%d ",t[i]);

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值