[JZOJ5638][九省联考D1T2] IIIDX

题目大意

给你长度为n的一个序列a,以及一个参数K,需要把a重新排列,使得 a [ i ] >= a [ i k ] 。求字典序最大的重排序列。
这里写图片描述

解题思路

贪心。
我们从前往后放,每次放合法的尽量大的。
把大小关系抽象成树形关系。其中i的父亲是i/k。
首先考虑d[i]互不相同。
一开始什么都没放,第一个数我们选第size[1]大的数填,这样才能保证1号点的子树的元素都比它大,暂时屏蔽前size[1]-1个点,表示预留出来给子树填。接着考虑第二个,假如fa[2]!=1,那么屏蔽的点相当于不存在,同样地做即可。如果父亲进行了屏蔽操作,要把操作撤销一下。这个用权值线段树即可实现。当然也可以直接打个dfs做。
那么现在要考虑d[i]有相同的情况。(先把d降序排序)
比如这种情况:
9 8 7 6 5 5 5 5 5 4 3 2
假如1号的size为7,那么按理说前7个应该屏蔽。但是,如果2号不是1的儿子,这样不是最优的,应该以最后一个5为结尾,屏蔽后面的7个。这样才能使2号尽量大。(比如二号只有一个儿子,那么填9是可行的)
权值线段树是不能用了,想别的办法维护。
设f[i]表示d中,下标i以及其之前的位置,一定预留掉了的数量。比如上面,如果1填数,那么我们要给最后一个5到末尾的位置的f都加上7,表示在[1,8]中选出7个数。而前面不用管。毕竟,我们对i-f[i]做个后缀min,就可以得出一个位置前面至多有多少个位置能用。
我们用线段树维护i-f[i]的后缀min,这样我们可以快速得到一个位置前面还有多少个数能用。
每次我们填x的时候,要找到最前面的,后缀min{i-f[i]}>=size[x]的下标i,如果有和他相同的元素,使用相同元素下标最大的可用的i。就比如上面定位到了第3个5,我们使用第5个5,并且把第五个5标记为不可用。这样就可以维护了。
记得把父亲的预留撤销一下。
扫一遍就可以了。

代码

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<map>
#include<set>
using namespace std;
typedef long long ll;
typedef double db;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
#define cmax(a,b) (a=(a>b)?a:b)
#define cmin(a,b) (a=(a<b)?a:b)
const int N=2e6+5;
int czy[N],a[N],id[N],fa[N],tr[N*4],prt[N],n,col,siz[N],z,i,d[N],q1,q2,x,dfn[N],td,cnt,q3,dis[N],dec,f[N],e[N];
db K;
int fst[N],nxt[N],b[N],tt;
void cr(int x,int y)
{
    tt++;
    b[tt]=y;
    nxt[tt]=fst[x];
    fst[x]=tt;
}
bool cmp(int x,int y)
{
    return a[x]<a[y];
}
void lsh()
{
    int i;
    fo(i,1,n) id[i]=i;
    sort(id+1,id+1+n,cmp);
    fo(i,1,n)
    {
        if (czy[a[id[i-1]]]!=a[id[i]]) col++;
        czy[col]=a[id[i]];
        a[id[i]]=col;
    }
}
void change(int x,int l,int r,int p,int v)
{
    if (l==r) tr[x]+=v;
    else
    {
        int m=l+r>>1;
        if (p<=m) change(x*2,l,m,p,v);
        else change(x*2+1,m+1,r,p,v);
        tr[x]=tr[x*2]+tr[x*2+1];
    }
}
int grab(int x,int l,int r,int v)
{
    if (l==r) return l;
    int m=l+r>>1;
    if (tr[x*2+1]>=v) return grab(x*2+1,m+1,r,v);
    else return grab(x*2,l,m,v-tr[x*2+1]);
}
int process(int x,int l,int r,int p,int v,int id)
{
    if (l==r)
    {
        dec=min(tr[x],v);
        v-=dec;
        change(1,1,n,p,-dec);
        e[++e[0]]=p;
        f[e[0]]=dec;
        return v;
    }
    int m=l+r>>1;
    if (p<=m) return process(x*2,l,m,p,v,id);
    else return process(x*2+1,m+1,r,p,v,id);
}
void dfs(int x)
{
    siz[x]=1;
    dfn[x]=td++;
    for(int p=fst[x];p;p=nxt[p])
    {
        dis[b[p]]=dis[x]+1;
        dfs(b[p]);
        siz[x]+=siz[b[p]];
    }
}
void solve(int x) 
{
    z=grab(1,1,n,cnt+siz[x]);
    prt[x]=z;
    change(1,1,n,z,-1);
}
int main()
{
    freopen("iiidx.in","r",stdin);
    freopen("iiidx.out","w",stdout);
    scanf("%d %lf",&n,&K);
    fo(i,1,n) scanf("%d",a+i);
    lsh();
    fo(i,1,n) change(1,1,n,a[i],1);
    fo(i,1,n)
        fa[i]=trunc(db(i)/K+1e-4);
    fd(i,n,1) cr(fa[i],i);
    dfs(0);
    d[1]=0;
    q1=1;
    q2=1;
    while (q1<=q2)
    {
        q3=q2;
        cnt=0;
        fo(i,q1,q3)
        {
            x=d[i];
            if (x==26666)
            {
                a[0]=0;
            }
            if (x) 
            {
                solve(x);
                cnt+=process(1,1,n,prt[x],siz[x]-1,dis[x]);
            }
            for(int p=fst[x];p;p=nxt[p])
            {
                dis[b[p]]=dis[x]+1;
                d[++q2]=b[p];
            }
        }
        while (e[0])
        {
            change(1,1,n,e[e[0]],f[e[0]]);
            e[0]--;
        }
        q1=q3+1;
    }
    fo(i,1,n)
        printf("%d\n",czy[prt[i]]);
}

猜你喜欢

转载自blog.csdn.net/zltjohn/article/details/79857518
今日推荐