题目大意
给定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;
}