• loj2472[九省联考2018]IIIDX


    题意:要求构造一个d的排列使得满足d[i/k]<=d[u]且字典序最大。

    标程(bzoj上并不能过):

     1 #include<bits/stdc++.h>
     2 #define mid ((l+r)>>1)
     3 using namespace std;
     4 const int N=500005;
     5 const int eps=1e-7;
     6 double k;
     7 int tag[N<<2],sum[N<<2],size[N],n,fa[N],ans[N],d[N];
     8 map<int,int> to;
     9 bool cmp(int A,int B){return A>B;}
    10 void up(int k) {sum[k]=min(sum[k<<1],sum[k<<1|1]);}
    11 void down(int k)
    12 {
    13   if (tag[k])
    14   {
    15     tag[k<<1]+=tag[k];tag[k<<1|1]+=tag[k];
    16     sum[k<<1]+=tag[k];sum[k<<1|1]+=tag[k];
    17     tag[k]=0;
    18   }
    19 }
    20 void build(int k,int l,int r)
    21 {
    22   if (l==r) {sum[k]=l;return;}
    23   build(k<<1,l,mid);build(k<<1|1,mid+1,r);
    24   up(k);
    25 }
    26 void add(int k,int l,int r,int L,int R,int x)
    27 {
    28   if (L<=l&&r<=R) {sum[k]+=x;tag[k]+=x;return;}
    29   down(k);
    30   if (L<=mid) add(k<<1,l,mid,L,R,x);
    31   if (R>mid) add(k<<1|1,mid+1,r,L,R,x); 
    32   up(k);
    33 } 
    34 int qry(int k,int l,int r,int x)
    35 {
    36   if (l==r) return (x<=sum[k])?l:l+1;
    37   down(k);int res;
    38   if (x<=sum[k<<1|1]) res=qry(k<<1,l,mid,x);
    39   else res=qry(k<<1|1,mid+1,r,x);
    40   up(k);//由于前面down标记,查询时也需要up来更新
    41   return res;
    42 }
    43 int main()
    44 { 
    45   scanf("%d%lf",&n,&k);
    46   for (int i=1;i<=n;i++) scanf("%d",&d[i]),fa[i]=(int)((double)i/k+eps);
    47   for (int i=n;i>=1;i--) size[i]++,size[fa[i]]+=size[i];
    48   build(1,1,n);
    49   sort(d+1,d+n+1,cmp);
    50   for (int i=n;i>=1;i--) if (d[i]!=d[i+1]) to[d[i]]=i; 
    51   for (int i=1;i<=n;i++)
    52   {
    53     if (fa[i]&&fa[i]!=fa[i-1]) add(1,1,n,ans[fa[i]],n,size[fa[i]]-1);
    54     int t=qry(1,1,n,size[i]); ans[i]=to[d[t]]--;
    55     add(1,1,n,ans[i],n,-size[i]);
    56   }
    57   for (int i=1;i<=n;i++) printf("%d ",d[ans[i]]);
    58   return 0;
    59 }
    View Code

    题解:线段树+占位

    如果直接贪心:从上到下贪心然后把最小的派给编号最大的是不对的。1 1 1 2 应该是1 1 2 1。

    直接按编号从小到大考虑。每一层用size占位,线段树维护从小到大的d序列,每一位表示当前位前面有多少个数没被使用。每次选择最靠前的一个sum[x]>=size[i](单调),如果找到x周围有若干个和sum[x]相等,那么选取最靠后的一个(贪心)。如果当前子树的父亲已经处理完毕,那么还原,继续处理儿子。

  • 相关阅读:
    工具:统计jQuery中各字符串出现次数
    读Ext之八(原生事件对象的修复及扩充)
    querySelector和getElementById通过id获取元素的区别
    读Ext之十(解析JSON)
    Safari/Chrome中placeholder属性实现不完整
    读Ext之十一(通过innerHTML创建元素)
    各浏览器中innerHTML实现差异(2)
    读Ext之五(Dom的低级封装)
    读Ext之十二(在各个位置插入元素)
    读Ext之四(事件的低级封装)
  • 原文地址:https://www.cnblogs.com/Scx117/p/9197326.html
Copyright © 2020-2023  润新知