BZOJ5249:[2018多省省队联测]IIIDX-线段树

传送门

题意:

你需要给给正在制作中的游戏《IIIDX》安排曲目的解锁顺序。游戏内共有n首曲目,每首曲目都会有一个难度d,游戏内第i首曲目会在玩家Pass第 i k 首曲目后解锁。

安排这些曲目的顺序,使得每次解锁出的子的难度不低于作为条件需要玩家通关的曲子的难度,即使得确定顺序后的曲目的难度对于每个i满足 d i d i k

输出字典序最小的方案。

1 n 500000

Solution:

考场上一眼了一个做法:将权值从大到小排序,对于每个点贪心的放最大的符合条件的数,然后递归下去

但是这样的做法只适合于d互不相同的情况,对于d相同的情况,有可能x及x的子树并不需要填最大的值,和x同深度的x+1可能能填更大的数,举个栗子:

10 2.0
1 2 2 2 2 2 3 4 5 6

正解非常神奇:从小到大排序后,线段树维护 c i 表示第i个不同的权值往右还能取的权值的个数(包括本身),然后我们从小到大枚举每个点,对于每个点x,我们需要求一个最往右的位置pos满足pos及pos左边的所有 c i s i z e [ x ] ( s i z e [ x ] 为以x为根的子树大小),求出pos之后修改一下c数组,表示预留出x子树的点的位置,这些操作都可以通过线段树来实现

需要注意的是:如果一个点有父亲,那么需要先把他父亲为子树预留的东西去掉,而且每个父亲预留的大小只能去掉一次。

代码:

#include<cstdio>
#include<iostream>
#include<vector>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=500010;
int n,a[N],siz[N],x,ans[N],lsh[N],cnt;
int vis[N],fa[N];
bool tag[N];
vector<int> e[N];
struct tree{
    int l,r,v,tag;
}tr[4*N];
double k;
void update(int i)
{
    tr[i].v=min(tr[i<<1].v,tr[i<<1|1].v);
}
void build(int i,int l,int r)
{
    tr[i].l=l,tr[i].r=r;
    if (l==r) {tr[i].v=vis[l];return;}
    int mid=l+r>>1;
    build(i<<1,l,mid);build(i<<1|1,mid+1,r);
    update(i);
}
void dfs(int x)
{
    siz[x]=1;
    for (int i=0;i<(int)e[x].size();i++)
    {
        int y=e[x][i];fa[y]=x;
        dfs(y);
        siz[x]+=siz[y];
    }
}
void add(int i,int x)
{
    tr[i].v+=x;
    tr[i].tag+=x;
}
void pushdown(int i)
{
    if (tr[i].tag)
    {
        add(i<<1,tr[i].tag);add(i<<1|1,tr[i].tag);
        tr[i].tag=0;
    }
}
void modify(int i,int l,int r,int x)
{
    int L=tr[i].l,R=tr[i].r;
    if (L>r||l>R) return;
    if (l<=L&&R<=r) {add(i,x);return;}
    pushdown(i);
    modify(i<<1,l,r,x);modify(i<<1|1,l,r,x);
    update(i);
}
int query(int i,int pos)
{
    int L=tr[i].l,R=tr[i].r;
    if (L==R) return (tr[i].v>=pos)?L:L-1;
    pushdown(i);
    if (tr[i<<1].v>=pos) return query(i<<1|1,pos);else return query(i<<1,pos);
}
//void pr(int i)
//{
//  if (tr[i].l==tr[i].r) {printf("%d ",tr[i].v);return;}
//  pushdown(i);
//  pr(i<<1);pr(i<<1|1);
//}
int main()
{
//  freopen("iiidx.in","r",stdin);
//  freopen("iiidx.out","w",stdout);
    scanf("%d%lf",&n,&k);for (int i=1;i<=n;i++) scanf("%d",&a[i]);
    sort(a+1,a+1+n);for (int i=1;i<=n;i++) lsh[++cnt]=a[i];
    cnt=unique(lsh+1,lsh+1+cnt)-lsh-1;for (int x,i=1;i<=n;i++) x=lower_bound(lsh+1,lsh+1+cnt,a[i])-lsh,vis[x]++;
    for (int i=cnt;i>=1;i--) vis[i]+=vis[i+1];
    build(1,1,cnt);

    for (int i=1;i<=n;i++)
    {
        int y=(1.0*i/k+1e-8);
        e[y].push_back(i);
    }
    dfs(0);tag[0]=1;
    for (int i=1;i<=n;i++)
    {
        if (!tag[fa[i]]) tag[fa[i]]=1,modify(1,1,ans[fa[i]],siz[fa[i]]-1);
    //  cout<<i<<endl;
    //  pr(1);cout<<endl;
        ans[i]=query(1,siz[i]);
        modify(1,1,ans[i],-siz[i]);
    //  pr(1);cout<<endl;
    }
    for (int i=1;i<=n;i++) printf("%d ",lsh[ans[i]]);
}

猜你喜欢

转载自blog.csdn.net/charlie_jilei/article/details/79864247