2021-08-10 SSL 模拟赛 T3 (树状数组维护版)
怎么还是没有标题??啦啦啦我又回来了
本解法是暴力在时间复杂度边缘疯狂徘徊的蒟蒻。
本解法是树状数组的降维打击。
题目描述:戳这里
思路:
大致的思想在暴力版里我已将讲过了,可以去看看我的 暴力版
那么我们这是要维护一个动态的区间第 k 大数,使用树状数组。
我们维护序列,初始时在每个位置上+1,构成一个差分数列:
1
,
1
,
1
,
1
,
1
,
1
⋅
⋅
⋅
⋅
1,1,1,1,1,1····
1,1,1,1,1,1⋅⋅⋅⋅,然后对这个数列求前缀和 (使用树状数组去维护
),第 i 个位置的权值 表示它是第几小的数。
然后我们从后往前循环,根据我们的思路,我们可以清楚地知道当前要填的是剩下序列的第 k 大数,接着我们可以将其转换位 第 T 小数 (这样就可以直接维护前缀和了),然后我们在前缀和数列中二分查找第1 个
前缀等于 T 的位置 (记住一定是第一个),然后把它填到答案数组中,然后我们在这个前缀和数列中的这个位置-1,用树状数组维护其前缀和 (因为 -1 后这个点的前缀和上一个点相同,为了避免重复使用,所以二分时要找第一个相等的数据)
最后的时间复杂度为 O ( n ∗ l o g n ) O(n*log~n) O(n∗log 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;
}