[codeforces873E]Awards For Contestants

题目大意

给定n个正整数,要把它们放进三个组(分别为1号组,2号,3号),也可以不放。每组至少要有一个数。同时对于对于1,2,3号组,两两直接必须满足cnt[p]≤2*cnt[q],其中cnt[x]表示x号组有多少个数。对于两个数x,y,如果x < y,那么y不能放在编号比x大的组。
定义c[x]表示x号组的最大数,d[x]是x号组的最小数。特殊地,c[-1]表示没有放的数的最大值,d[-1]同理。现在求一个合法分配方案,最大化{d[1]-c[2],d[2]-c[3],d[3]-c[-1]}的字典序。

n≤3000,正整数大小不超过5000

分析

首先给n个数降序排序,容易发现这等价于把排序后的数组分成4段,其中前三段是要满足cnt两两不超过彼此两倍的条件。
然后可以枚举第1组的边界和第2组的边界,然后合法的第三组的边界就是一个区间,查询区间内的最大值即可。这里我写了个rmq。所以时间复杂度是 O(n2)

#include <cstdio>
#include <cstring>
#include <algorithm>

using namespace std;

const int N=3005,Log=12;

typedef long long LL;

int n,a[N],diff[N],m1,m2,m3,Ans[N],L[N],rmq[Log][N];

struct Data
{
    int x,ID;
}A[N];

bool operator < (const Data &a,const Data &b)
{
    return a.x<b.x;
}

int main()
{
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]); A[i].ID=i; A[i].x=a[i];
    }
    sort(A+1,A+n+1);
    for (int i=0;i<n;i++) rmq[0][i]=i,diff[i]=A[i+1].x-A[i].x;
    m1=-1;
    for (int i=1,j=0;i<=n;i<<=1,j++) L[i]=j;
    for (int i=3;i<=n;i++) if (!L[i]) L[i]=L[i-1];
    for (int j=1;j<Log;j++)
        for (int i=0;i<=n-(1<<j);i++)
            rmq[j][i]=(diff[rmq[j-1][i]]>=diff[rmq[j-1][i+(1<<j-1)]])?rmq[j-1][i]:rmq[j-1][i+(1<<j-1)];
    for (int i=n+1>>1;i<n;i++) if (m1==-1 || diff[i]>=diff[m1])
    {
        int j,k,l,r,p=n-i,q;
        for (int j=1;j<n;j++)
        {
            q=i-j;
            if (2*p<q || 2*q<p || j*2<q || j*2<p) continue;
            if (p>q) l=p+1>>1,r=q<<1;else l=q+1>>1,r=p<<1;
            if (l<=r)
            {
                r=max(0,j-r); l=max(0,j-l); swap(l,r);
                k=L[r-l+1];
                k=(diff[rmq[k][l]]>=diff[rmq[k][r-(1<<k)+1]])?rmq[k][l]:rmq[k][r-(1<<k)+1];
                if (m1==-1 || (diff[m1]<diff[i] || diff[m1]==diff[i] && (diff[m2]<diff[j] || diff[m2]==diff[j] && diff[m3]<diff[k])))
                {
                    m1=i; m2=j; m3=k;
                }
            }
        }
    }
    for (int i=n;i>m1;i--) Ans[A[i].ID]=1;
    for (int i=m1;i>m2;i--) Ans[A[i].ID]=2;
    for (int i=m2;i>m3;i--) Ans[A[i].ID]=3;
    for (int i=m3;i;i--) Ans[A[i].ID]=-1;
    for (int i=1;i<n;i++) printf("%d ",Ans[i]); printf("%d\n",Ans[n]);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值