zoukankan      html  css  js  c++  java
  • BZOJ4345 POI2016Korale(构造+堆+线段树)

      注意到k与n同阶,考虑构造一种枚举子集的方式,使得尽量先枚举较小的子集。首先sort一下,用堆维护待选子集。每次取出最小子集,并加入:1.将子集中最大数ai替换为ai+1 2.直接向子集中添加ai+1 这两个子集(若不存在ai+1则不操作)。如此操作k次即可得到第一问的答案。

      对于正确性,我们证明当删除一个子集后恰好比他大的下一个子集一定在堆中。采取归纳和反证。显然每个子集都可以由上面的构造方式变换得来。归纳基础显然。假设该子集和比它小的所有子集已被枚举,如果恰好比它大的这个子集不在堆里,则说明可以通过变换得到这个子集的子集均未被枚举,这些子集一定不大于当前子集,这与所有比它小的子集都已枚举矛盾。

      下面构造方案。只需要算出需要找该总和下第几小的方案,按字典序暴力dfs就可以了,dfs时保证总和不超过第一问的答案即可保证复杂度,找编号最小的可被加入的物品可以用线段树。开始懵逼了半天线段树在这有什么用,然后突然醒悟字典序是读入的而不是排序之后的……没救了。

    #include<iostream> 
    #include<cstdio>
    #include<cmath>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    #include<queue>
    using namespace std;
    int read()
    {
        int x=0,f=1;char c=getchar();
        while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
        while (c>='0'&&c<='9') x=(x<<1)+(x<<3)+(c^48),c=getchar();
        return x*f;
    }
    #define N 1000010
    #define ll long long
    int n,m,id[N],b[N],cnt,tot;
    int L[N<<2],R[N<<2],tree[N<<2];
    ll ans;
    struct data
    {
        ll x;int i;
        bool operator <(const data&a) const
        {
            return x>a.x;
        }
    }a[N];
    priority_queue<data> q;
    bool cmp(const data&a,const data&b)
    {
        return a.i<b.i;
    }
    void build(int k,int l,int r)
    {
        L[k]=l,R[k]=r;
        if (l==r) {tree[k]=a[l].x;return;}
        int mid=l+r>>1;
        build(k<<1,l,mid);
        build(k<<1|1,mid+1,r);
        tree[k]=min(tree[k<<1],tree[k<<1|1]);
    }
    int qmin(int k,int l,int r)
    {
        if (L[k]==l&&R[k]==r) return tree[k];
        int mid=L[k]+R[k]>>1;
        if (r<=mid) return qmin(k<<1,l,r);
        else if (l>mid) return qmin(k<<1|1,l,r);
        else return min(qmin(k<<1,l,mid),qmin(k<<1|1,mid+1,r));
    }
    int query(int k,int p,ll x)
    {
        if (L[k]==R[k]) return L[k];
        int mid=L[k]+R[k]>>1;
        if (p>mid) return query(k<<1|1,p,x);
        else if (qmin(k<<1,p,mid)<=x) return query(k<<1,p,x);
        else return query(k<<1|1,mid+1,x);
    }
    void dfs(int k,ll s)
    {
        if (tot==0) return;
        if (s==ans) {tot--;if (tot==0) for (int i=1;i<=cnt;i++) printf("%d ",id[i]);return;}
        int p=query(1,k+1,ans-s);
        while (p<=n)
        {
            id[++cnt]=p;
            dfs(p,s+b[p]);if (tot==0) return;
            cnt--;
            p=query(1,p+1,ans-s);
        }
    }
    int main()
    {
    #ifndef ONLINE_JUDGE
        freopen("bzoj4345.in","r",stdin);
        freopen("bzoj4345.out","w",stdout);
        const char LL[]="%I64d
    ";
    #else
        const char LL[]="%lld
    ";
    #endif
        n=read(),m=read();
        for (int i=1;i<=n;i++) b[i]=a[i].x=read(),a[i].i=i;
        a[n+1].x=0,a[n+1].i=n+1;build(1,1,n+1);
        sort(a+1,a+n+1);reverse(a+1,a+n+1);
        q.push((data){a[1].x,1});
        for (int i=1;i<m;i++)
        {
            data x=q.top();q.pop();
            if (x.x>ans) tot=0;
            ans=x.x;tot++;
            if (x.i<n) q.push((data){x.x-a[x.i].x+a[x.i+1].x,x.i+1}),q.push((data){x.x+a[x.i+1].x,x.i+1});
        }
        cout<<ans<<endl;
        dfs(0,0);
        return 0;
    }
  • 相关阅读:
    个人冲刺计划一周天详细
    软件小创意
    电梯调度小程序。
    敏捷开发一些百科。
    求二维数组的子数组中的最大值!
    求整数数组中子数组最大的和值!
    单元测试我们应该注意什么!
    分析一个文档(英语文章)中各个词出现的频率,并打印频率最高的前10个。
    有感而发
    冲刺一TD美景美图
  • 原文地址:https://www.cnblogs.com/Gloid/p/9861480.html
Copyright © 2011-2022 走看看