【bzoj5249】[2018多省省队联测]IIIDX

5249: [2018多省省队联测]IIIDX

Time Limit: 40 Sec   Memory Limit: 512 MB
Submit: 499   Solved: 236
[ Submit][ Status][ Discuss]

Description

【题目背景】
Osu听过没?那是Konano最喜欢的一款音乐游戏,而他的梦想就是有一天自己也能做个独特酷炫的音乐游戏。现在
,他在世界知名游戏公司KONMAI内工作,离他的梦想也越来越近了。这款音乐游戏内一般都包含了许多歌曲,歌曲
越多,玩家越不易玩腻。同时,为了使玩家在游戏上氪更多的金钱花更多的时间,游戏一开始一般都不会将所有曲
目公开,有些曲目你需要通关某首特定歌曲才会解锁,而且越晚解锁的曲目难度越高。
【题目描述】
这一天,Konano接到了一个任务,他需要给正在制作中的游戏《IIIDX》安排曲目的解锁顺序。游戏内共有n首曲目
,每首曲目都会有一个难度d,游戏内第i首曲目会在玩家Pass第trunc(i/k)首曲目后解锁(x为下取整符号)若tru
nc(i/k)=0,则说明这首曲目无需解锁。举个例子:当k=2时,第1首曲目是无需解锁的(trunc(1/2)=0),第7首曲
目需要玩家Pass第trunc(7/2)=3首曲目才会被解锁。Konano的工作,便是安排这些曲目的顺序,使得每次解锁出的
曲子的难度不低于作为条件需要玩家通关的曲子的难度,即使得确定顺序后的曲目的难度对于每个i满足Di≥Dtrun
c(i/k)。当然这难不倒曾经在信息学竞赛摸鱼许久的Konano。那假如是你,你会怎么解决这份任务呢

Input

第1行1个正整数n和1个小数k,n表示曲目数量,k其含义如题所示。
第2行n个用空格隔开的正整数d,表示这n首曲目的难度。
1 ≤ n ≤ 500000
1 < k ≤ 10^9
1 < d ≤ 10^9

Output

输出1行n个整数,按顺序输出安排完曲目顺序后第i首曲目的难度。
若有多解,则输出d1最大的;若仍有多解,则输出d2最大的,以此类推。

Sample Input

4 2.0
114 514 1919 810

Sample Output

114 810 514 1919

HINT

Source

[ Submit][ Status][ Discuss]



。。。我觉得这个傻题,没啥好讲的吧。。


呵呵呵呵呵呵呵呵


我感觉大家好像解题过程都一样啊

先看题——>诶sb贪心?——>嘿嘿嘿码码码——>啥?为啥只有55?——>啊?还有反例?——>GG 55分滚粗(除了某些神仙


首先这题如果直接贪心显然不行啊

比如:

4 2

1 1 1 2

嘿嘿嘿?是不是被hack了?


这题正解是线段树

核心思想是一个类似“预定”的东西


首先这个可以看成树一样的东西,具体来说,对于点x来说,他的父亲是点(x / k)


我们先从小到大排序一遍
然后设f[i]为右侧的可用的点的数量(初始时为n - i + 1)

对于一个点u,我们考虑它的子树中必须要有siz[u] - 1个点比它来的大

那我们就在序列中二分一个点x,使得[1,x]的f值均大于siz[u]

考虑到如果有多个这样的x,x应该尽量地大

这样完了以后我们考虑这个x尽量大的同时,还应当尽量靠左

比如说:1 3 4 5 5 8,如果我们选到的最大的x为第5个位置上的5,为了方便后面的点的选取,x应当取第4个位置上的5(这个很显然吧

然后在线段树上把这个[1,x]的f全都减去siz[u](我们称为预定

对应到上面那个例子,就是把[1,4]的f全都减去siz[4]


然后对于点u的儿子,我们需要先把点u预定的给去除掉,即把[1,x]加一个(siz[x] - 1),然后再预定,这样一直做下去就好了


PS.垃圾bzoj严重卡精度,最好除的时候最好加一个eps


代码:

#include<queue>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<cmath>
using namespace std;

typedef long long LL;
typedef double DL;

const int INF = 2147483647;
const int maxn = 500010;
const int segn = 4 * maxn;
const DL eps = 1e-5;

DL k;
vector<int> e[maxn];
int n,a[maxn];
int fr[maxn],siz[maxn],fa[maxn],used[maxn];
int data[maxn],cnt[maxn],N;
int minx[segn],set[segn];

inline LL getint()
{
    LL ret = 0,f = 1;
    char c = getchar();
    while (c < '0' || c > '9') 
    {
        if (c == '-') f = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9')
        ret = ret * 10 + c - '0',c = getchar();
    return ret * f;
}

inline void add(int o,int x)
{
	minx[o] += x;
	set[o] += x;
}

inline void pushdown(int o)
{
	if (set[o])
	{
		int lc = o << 1,rc = o << 1 | 1;
		add(lc,set[o]);
		add(rc,set[o]);
		set[o] = 0;
	}
}

inline void maintain(int o)
{
	int lc = o << 1,rc = o << 1 | 1;
	minx[o] = min(minx[lc],minx[rc]);
}

inline void modify(int o,int l,int r,int al,int ar,int x)
{
	if (al > ar) return;
	if (al <= l && r <= ar) {add(o,x); return;}
	pushdown(o);
	int lc = o << 1,rc = o << 1 | 1,mid = l + r >> 1;
	if (al <= mid) modify(lc,l,mid,al,ar,x);
	if (mid < ar) modify(rc,mid + 1,r,al,ar,x);
	maintain(o);
}

inline int Query(int o,int l,int r,int x)
{
	if (l == r) return minx[o] >= x ? l : l - 1;
	pushdown(o);
	int mid = l + r >> 1,lc = o << 1,rc = o << 1 | 1;
	if (minx[lc] >= x) return Query(rc,mid + 1,r,x);
	else return Query(lc,l,mid,x); 
}

inline void build(int o,int l,int r)
{
	if (l == r) {minx[o] = n - l + 1; return;}
	int mid = l + r >> 1,lc = o << 1,rc = o << 1 | 1;
	build(lc,l,mid);
	build(rc,mid + 1,r);
	maintain(o);
}

inline void dfs(int u)
{
	siz[u] = 1;
	for (int i = 0; i < e[u].size(); i++)
	{
		int v = e[u][i];
		dfs(v);
		siz[u] += siz[v];
	}
}

inline void print(int x)
{
	if (x >= 10) print(x / 10);
	putchar(x % 10 + '0');
}

inline int equ(DL x,DL y)
{
	return fabs(x - y) <= eps;
}

int main()
{
    n = getint(); scanf("%lf",&k);
    for (int i = 1; i <= n; i++)
    	a[i] = getint();
    sort(a + 1,a + n + 1);
    for (int i = 1; i <= n; i++)
    {
    	if (i == 1 || a[i] != a[i - 1]) cnt[i] = 0;
    	else cnt[i] = cnt[i - 1] + 1;
	}
	
    for (int i = 1; i <= n; i++)
    {
    	fa[i] = equ(floor(i / k) + 1,i / k) ? floor(i / k) + 1 : floor(i / k);
		e[fa[i]].push_back(i);
	}
	dfs(0);
	build(1,1,n);
	
	for (int i = 0; i <= n; i++)
	{
		if (!used[fa[i]] && fa[i])
		{
			used[fa[i]] = 1;
			modify(1,1,n,1,fr[fa[i]],siz[fa[i]] - 1);
		}
		if (i)
		{
			int tmp = Query(1,1,n,siz[i]);
			tmp -= cnt[tmp];
			fr[i] = tmp + cnt[tmp];
			cnt[tmp]++;
			modify(1,1,n,1,fr[i],-siz[i]);
		}
	}
	
	for (int i = 1; i <= n; i++)
		print(a[fr[i]]) , putchar(' ');
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值