2021-08-10 SSL 模拟赛 T3 (树状数组维护版)(最优解)

2021-08-10 SSL 模拟赛 T3 (树状数组维护版)

怎么还是没有标题??啦啦啦我又回来了
本解法是暴力在时间复杂度边缘疯狂徘徊的蒟蒻。
本解法是树状数组的降维打击。
在这里插入图片描述

题目描述:戳这里

思路:

大致的思想在暴力版里我已将讲过了,可以去看看我的 暴力版
那么我们这是要维护一个动态的区间第 k 大数,使用树状数组。

我们维护序列,初始时在每个位置上+1,构成一个差分数列: 1 , 1 , 1 , 1 , 1 , 1 ⋅ ⋅ ⋅ ⋅ 1,1,1,1,1,1···· 111111,然后对这个数列求前缀和 (使用树状数组去维护),第 i 个位置的权值 表示它是第几小的数

然后我们从后往前循环,根据我们的思路,我们可以清楚地知道当前要填的是剩下序列的第 k 大数,接着我们可以将其转换位 第 T 小数 (这样就可以直接维护前缀和了),然后我们在前缀和数列中二分查找第1 个前缀等于 T 的位置 (记住一定是第一个),然后把它填到答案数组中,然后我们在这个前缀和数列中的这个位置-1,用树状数组维护其前缀和 (因为 -1 后这个点的前缀和上一个点相同,为了避免重复使用,所以二分时要找第一个相等的数据)

最后的时间复杂度为 O ( n ∗ l o g   n ) O(n*log~n) O(nlog n)

代码:

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<queue>
#include<cstring>
#include<ctime>
#define r register
#define rep(i,x,y) for(r ll i=x;i<=y;++i)
#define per(i,x,y) for(r ll i=x;i>=y;--i)
using namespace std;
typedef long long ll;
const ll V=1e4+100;
ll n,a[V],c[V],f[V];
ll lowbit(ll x)
{
	return x&-x;
}
void add(ll x,ll y)
{
	for(;x<=n;x+=lowbit(x))
	 c[x]+=y;
}
ll ask(ll x)
{
	ll res=0;
	for(;x;x-=lowbit(x))
	  res+=c[x];
	return res;
}
ll find(ll v) //二分查找
{
	ll x=1,y=n;
	while(x<=y)
	{
		ll mid=x+y>>1;
		ll val=ask(mid);
		if(val<v) x=mid+1;
		else if(val>v) y=mid-1;
		else 
		{
			while(ask(mid-1)==val) --mid;
			return mid;
		}
	}
	return -1;
}
int main()
{
	scanf("%lld",&n);
	rep(i,1,n)
	{
		scanf("%lld",&a[i]);
		add(i,1);//初始序列
	}
	per(i,n,1)
	{
		ll v=i-a[i]; //转化成第 k 小数
		f[i]=find(v);
		add(f[i],-1);  //从数列中删除这个数
	}
	rep(i,1,n)
	 printf("%lld ",f[i]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值