[九省联考2018]iiidx (线段树+贪心)

题目大意:给你一个序列,让你对它重新排列,保证a[i]<=a[i/k],求字典序最大的排列

洛谷传送门​​​​​​​

把i/k和i连边,发现形成了一个类似于小根堆的树形结构

先是一个错误的贪心:贪心每次选择前size[x]大个数依次填到树里

这种方法在有重复数字的时候会出锅,比如1112,如果用上面的方法就是1112,但正确的是1121

原因呢,就是填满一颗子树内并不一定要用完偏大的,可能和子树根同一深度的另一个节点可以取到更大的,比如有很多1,很少的2,我可能只要一些1就能把这棵子树填满,然后留给一些2给后面的子树去填。

然后我想了一个splay维护的贪心,就是相同的只取能填满子树且偏小的,但被我自己hack掉了,因为后面的子树不一定用光大的,可以把大的留给前面的来保证字典序最大

所以题解的方法还是很神的,用线段树来维护这个贪心

建一颗维护最小值和区间修改的线段树,维护一个变量F[i]表示 i 之前还有多少个节点可用

每次预留出size[x]个,在线段树上二分找到一个最小位置 i 满足F[k]>=size[x](k=i...n),那么这个点的答案就是a[i],如果有多个相同的,就找最后一个,可以预处理一个Last来解决,如果取过了某个节点就lasta[a[i]]--(因为所有相同的a[i]是等价的所以不用考虑位置),a[i]比较大需要离散。然后区间修改i...n全部减掉size[x]

然后遍历到子节点时,如果父节点的预留没有被加回来,就把父节点预留的部分重新加回来size[fa]-1个(父节点自己要占一个)

其实这可以看成对树进行BFS的过程,要保证同一深度的节点都能取到最大值,所以有了“预留”这种操作来防止不同子树间的冲突

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4 #define il inline
  5 #define dd double
  6 #define N 501000
  7 using namespace std;
  8 //re
  9 int n,cte,cnt;dd K;
 10 int a[N],org[N],lst[N],sz[N],head[N],ans[N],fa[N],add[N],id[N];
 11 struct node{int id,val;}b[N];
 12 struct Edge{int to,nxt;}edge[N];
 13 void ae(int u,int v){
 14     cte++;edge[cte].to=v;
 15     edge[cte].nxt=head[u],head[u]=cte;
 16 }
 17 int cmp(int x,int y){return x>y;}
 18 int cmp1(node x,node y){return x.val<y.val;}
 19 int cmp2(node x,node y){return x.id<y.id;}
 20 struct Seg{
 21     int mi[N<<2],tag[N<<2];
 22     il void pushup(int rt){mi[rt]=min(mi[rt<<1],mi[rt<<1|1]);}
 23     il void pushdown(int rt)
 24     {
 25         if(!tag[rt]) return;
 26         tag[rt<<1]+=tag[rt],tag[rt<<1|1]+=tag[rt];
 27         mi[rt<<1]+=tag[rt],mi[rt<<1|1]+=tag[rt];
 28         tag[rt]=0;
 29     }
 30     void build(int l,int r,int rt)
 31     {
 32         if(l==r){mi[rt]=l;return;}
 33         int mid=(l+r)>>1;
 34         build(l,mid,rt<<1);
 35         build(mid+1,r,rt<<1|1);
 36         pushup(rt);
 37     }
 38     void update(int L,int R,int l,int r,int rt,int w)
 39     {
 40         if(L<=l&&r<=R){mi[rt]+=w,tag[rt]+=w;return;}
 41         int mid=(l+r)>>1;
 42         pushdown(rt);
 43         if(L<=mid) update(L,R,l,mid,rt<<1,w);
 44         if(R>mid) update(L,R,mid+1,r,rt<<1|1,w);
 45         pushup(rt);
 46     }
 47     int find(int w,int l,int r,int rt)
 48     {
 49         if(l==r) return mi[rt]>=w?l:l+1;
 50         int mid=(l+r)>>1;
 51         pushdown(rt);
 52         if(mi[rt<<1|1]>=w) return find(w,l,mid,rt<<1);
 53         else return find(w,mid+1,r,rt<<1|1);
 54         pushup(rt);
 55     }
 56 }seg;
 57 int gint()
 58 {
 59     int ret=0,fh=1;char c=getchar();
 60     while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();}
 61     while(c>='0'&&c<='9'){ret=(ret<<3)+(ret<<1)+c-'0';c=getchar();}
 62     return ret*fh;
 63 }
 64 void dfs1(int u)
 65 {
 66     for(int j=head[u];j;j=edge[j].nxt){
 67         int v=edge[j].to;
 68         dfs1(v);sz[u]+=sz[v];
 69     }sz[u]++;
 70 }
 71 void descrete()
 72 {
 73     sort(b+1,b+n+1,cmp1);
 74     for(int i=1;i<=n;i++){
 75         if(b[i].val!=b[i-1].val)
 76             org[++cnt]=b[i].val;
 77         a[i]=cnt;
 78     }
 79 }
 80 void solve()
 81 {
 82     for(int i=1;i<=n;i++)
 83     {
 84         if(fa[i]&&!add[fa[i]]) 
 85             seg.update(id[fa[i]],n,1,n,1,sz[fa[i]]-1),add[fa[i]]=1;
 86         int x=seg.find(sz[i],1,n,1);
 87         ans[i]=a[x],id[i]=x;
 88         seg.update(lst[ans[i]],n,1,n,1,-sz[i]);
 89         lst[ans[i]]--;
 90     }
 91 }
 92 
 93 int main()
 94 {
 95     scanf("%d%lf",&n,&K);
 96     for(int i=1;i<=n;i++)
 97         b[i].val=gint(),b[i].id=i;
 98     descrete();
 99     sort(a+1,a+n+1,cmp);
100     for(int i=1;i<=n;i++)
101         lst[a[i]]=i;
102     for(int i=n;i>=1;i--)
103         fa[i]=(int)(1.0*i/K),ae(fa[i],i);
104     dfs1(0);
105     seg.build(1,n,1);
106     solve();
107     for(int i=1;i<=n;i++)
108         printf("%d ",org[ans[i]]);
109     puts("");
110     return 0;
111 }

 

posted @ 2018-10-09 15:43  guapisolo  阅读(279)  评论(0编辑  收藏  举报