【题解】 P4364 [九省联考 2018] IIIDX

首先把题目转化成给一棵树赋权值,让他成为一个小根堆,字典序最大。

考虑贪心,对于当前点 \(u\) 的子树,选择当前最大的 \(siz_u\) 个权值,然后递归求解。这样在 \(d_i\) 互不相同的时候容易发现是正确的。

如果 \(d_i\) 中存在相同的,我们发现当在决策 \(u\) 的子树时,子树内可以选择和 \(u\) 相等的权值,把大的权值让给和 \(u\) 的同深度的其他点(显然点的编号越小深度越小)。

这个时候我们就不能递归每个子树了,正确的做法是按照点的编号来决策。

先将 \(d\) 从小到大排序。

假设当前点是 \(u\) ,我们找到最大的可能权值在排好序的数组中的最小位置,让 \(u\) 选择这个位置,然后在 \(u\) 后面预留出 \(siz_u\) 个位置,这个可以通过维护 \(f_i\) 表示 \(i\) 后面最多有多少个剩余位置来实现,预留就是在 \([1,i-1]\) 区间减 \(1\) ,用线段树维护。

可是这样我们发现 \(i\) 的值不是真实的 \(f\) ,同时因为我们无法确定预留了哪些权值,所以真实的 \(f\) 没办法直接维护。

这个时候我们让之前所有预留的值都尽量靠前选(跳过 \(i\) ),这是为了让当前的决策点尽量靠后,此时的 \(f_i\) 就是维护的数组的前缀最小值,这个模拟一下这个过程就能得出。

我们发现 \(f_i\) 具有单调性,所以决策每个点的时候在线段树上二分就可以了。

注意在决策点 \(u\) 时要把父亲为他预留的位置空出来。

时间复杂度 \(O(n\log n)\)

\(\sf{Code}\)


#include<bits/stdc++.h>
#define N 2001001
#define MAX 2001
using namespace std;
typedef long long ll;
typedef double db;
const ll inf=1e18;
inline void read(ll &ret)
{
	ret=0;char c=getchar();bool pd=false;
	while(!isdigit(c)){pd|=c=='-';c=getchar();}
	while(isdigit(c)){ret=(ret<<1)+(ret<<3)+(c&15);c=getchar();}
	ret=pd?-ret:ret;
	return;
}
ll n,d[N],pos[N],siz[N],a[N];
db k;
struct node
{
	ll minn,tag,ls;
}seg[N];
inline node operator +(node x,node y)
{
	return node{min(x.minn,y.minn),0ll,x.ls};
}
inline void add(ll pos,ll num)
{
	seg[pos].minn+=num;
	seg[pos].ls+=num;
	seg[pos].tag+=num;
	return;
}
inline void pushdown(ll pos)
{
	add(pos<<1,seg[pos].tag);
	add(pos<<1|1,seg[pos].tag);
	seg[pos].tag=0;
	return;
}
inline void build(ll pos,ll l,ll r)
{
	if(l==r)
		seg[pos].minn=seg[pos].ls=n-l+1;
	else
	{
		ll mid=l+r>>1;
		build(pos<<1,l,mid);
		build(pos<<1|1,mid+1,r);
		seg[pos]=seg[pos<<1]+seg[pos<<1|1];
	}
	return;
}
inline void upgrade(ll pos,ll l,ll r,ll s,ll t,ll num)
{
	if(l>=s&&r<=t)
		return add(pos,num);
	else if(l>t||r<s)
		return;
	pushdown(pos);
	ll mid=l+r>>1;
	upgrade(pos<<1,l,mid,s,t,num);
	upgrade(pos<<1|1,mid+1,r,s,t,num);
	seg[pos]=seg[pos<<1]+seg[pos<<1|1];
	return;
}
inline ll query(ll pos,ll l,ll r,ll siz,ll d)
{
	if(l==r)
		return l;
	ll mid=l+r>>1;
	pushdown(pos);
	if(siz<=min(seg[pos<<1].minn,min(d,seg[pos<<1|1].ls)))
		return query(pos<<1|1,mid+1,r,siz,min(d,seg[pos<<1].minn));
	return query(pos<<1,l,mid,siz,d);
}
bool vis[N];
map<ll,ll>mp;
signed main()
{
	read(n);
	cin>>k;
	for(int i=1;i<=n;i++)
		read(a[i]);
	sort(a+1,a+n+1);
	for(int i=1;i<=n;i++)
	{
		if(!mp[a[i]])
			mp[a[i]]=i;
	}
	build(1,1,n);
	for(int i=1;i<=n;i++)
		siz[i]=1;
	for(int i=n;i;i--)
		siz[ll(floor(i/k))]+=siz[i];
	for(int i=1;i<=n;i++)
	{
		ll fa=ll(floor(i/k));
		if(fa)
			upgrade(1,1,n,1,pos[fa],siz[i]);
		ll tmp=a[query(1,1,n,siz[i],inf)];
		pos[i]=mp[tmp];
		mp[tmp]++;
		upgrade(1,1,n,1,pos[i],-siz[i]);
	}
	for(int i=1;i<=n;i++)
		printf("%lld ",a[pos[i]]);
	exit(0);
}
posted @ 2022-04-21 17:06  CelticOIer  阅读(31)  评论(0编辑  收藏  举报