D. Restore Permutation(树状数组+二分)

传送门
在这里插入图片描述
在这里插入图片描述
题意:
给一个数N和N个数每个数记录的是原数列N中该位置之前比其小的数之和 问原数列是多少 原数列每个数大小在1-N之间
例子
3
原数列 3 2 1
所以该数列为 0 0 0 以下同理
思路:
刚开始我推导出了最后一个数一定可以被确定下来,根据si=(n-1)*n/2可以计算出n 然后再用这个数推导前面的但是怎么推导呢?起初我并没有想过用1-N的前项和去推可能是因为觉得这个数列顺序变了那每个位置的和也会发生变化,但是后来发现这个题可以用二分解决的,从最后一个位置向前推,最后一个确认后,我们就将这个数从1-N的序列和中减去,然后继续二分当前位置的值,如果他之前的的和小于等于输入的num[i]就可以更新区间,最后得到答案。这道题也可以用线段树来写,我太菜了,总觉得那是模板不会维护,还是慢慢刷题吧,二分真的是个很重要的思想 0.0

代码

#include<cstdio>
#include<iostream>
#include<cstring>
#include<string>
#include<algorithm>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<cmath>
#define Xiaobo main
#define IO cin.tie(0),cout.tie(0),ios::sync_with_stdio(false);
using namespace std;
const int maxn=2e5+7;
const int mod=1e9+7;
const double eps=1e-15;
const double pi=acos(-1);
const int INF=0x3f3f3f;
typedef long long ll;
ll read(){ll c = getchar(),Nig = 1,x = 0;while(!isdigit(c) && c!='-')c = getchar();if(c == '-')Nig = -1,c = getchar();while(isdigit(c))x = ((x<<1) + (x<<3)) + (c^'0'),c = getchar();return Nig*x;}
ll qpow(ll a,ll b,ll m){ ll ans=1; while(b){ if(b&1) ans=ans%a%m;a=a*a%m,b>>=1;} return ans; }
ll qpow(ll a,ll b){ ll ans=1;while(b>0){ if(b&1) ans=ans*a;a*=a,b>>=1; } return ans;}
ll gcd(ll a,ll b){ return b==0?a:gcd(b,a%b);}
ll mul(ll a,ll b,ll m){ll res=0;while(b>0) { if(b&1) res=(res+a)%m;a=(a+a)%m;b>>=1;}return res;}
int lowbit(int x) { return x&(-x); }
ll shu[maxn];
ll num[maxn];
int n,m,t;
void add(int x,int y) { for(;x<n;x+=lowbit(x)) shu[x]+=y;}
ll qry(int x) { ll ans=0;while(x)ans+=shu[x],x-=lowbit(x);return ans;}
//思路 用1推n 最后一个数是可以确定的,利用最后一个数更新,找前一个数 
//二分答案,树状数组存储前n项的和快速查询,如果前mid的和等于num[i]那么证明这个位置就是mid
//维护树状数组   如果这个数被确定了 那就要更新树状数组将这个数删除掉 
//试想 线段树怎么写 0.0 
int Xiaobo()
{
	IO;
	cin>>n;
	for(int i=1;i<=n;i++) {
		add(i,i);
		cin>>num[i];
	}
	for(int i=n;i>=1;i--) {
		int l=1,r=n,ans=0;
		while(l<=r) {
			int mid=(l+r)>>1;
			if(qry(mid-1)<=num[i]) {
				l=mid+1;
				ans=mid;
			}
			else r=mid-1;
		}
		num[i]=ans;
		add(ans,-ans);
	}
	for(int i=1;i<=n;i++) cout<<num[i]<<" "; 
}

加油!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值