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]相等,那么选取最靠后的一个(贪心)。如果当前子树的父亲已经处理完毕,那么还原,继续处理儿子。

转载于:https://www.cnblogs.com/Scx117/p/9197326.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值