Acwing244 树状数组+二分

题目链接:https://www.acwing.com/problem/content/245/

思路一:
这道题目经过简化后,我发现这道题目其实是想要我们去模拟一个过程,即不断维护一个严格递增的序列,如果遍历到了第i个数,则将这个数字放到第a[i]+1(下标从1开始)的位置,然后让原来的a[i] + 1 - i位置的数往后移动一个单位,然后我就想到者相当与就是一个区间增加的过程,最后用树状数组去维护差分数组写出来下面的代码:

#include <iostream>
#include <cstdio>

using namespace std;

const int maxn = 1e5 + 5;

int l, r, n;
int a[maxn], b[maxn], c[maxn];

inline int lowbit(int x) { return x & -x; }
inline void change(int idx, int x) {
	for(; idx <= n; idx += lowbit(idx))
		c[idx] += x;
}
inline int query(int x) {
	int res = 0;
	while(x) {
		res += c[x];
		x -= lowbit(x);
	}
	
	return res;
} 

int main(void) {
//	freopen("in.txt", "r", stdin);
	scanf("%d", &n);
	for(int i = 2; i <= n; i ++) {
		scanf("%d", &a[i]);
		b[i] = a[i] - a[i - 1];
	}

	for(int i = 1; i <= n; i ++)
		change(i, b[i]);
			
	for(int i = 1; i <= n; i ++) {
		if(a[i] < i - 1) {
			l = a[i] + 1, r = i - 1;
			change(l, 1), change(r + 1, -1);
		}
	}
	
	for(int i = 1; i <= n; i ++)
		a[i] = query(i);

	for(int i = 1; i <= n; i ++)
		if(i == n) printf("%d", a[i] + 1);
		else printf("%d\n", a[i] + 1);
	
	return 0;
} 

然而这份代码是有问题的,因为在不断去交换的过程中,我的这份代码仅仅只是使用了一个差分,没有考虑到数字之间的交换,即忽略了这种情况,如果第j位的数需要到第j位上,然后第k位置的数导致了从j - 2 到 j + 2的数字需要往后移动一个位置,这时候,其实我们移动的依然是原来在第j位上的数,但是实际上,原来第j位的数应该到第i位上,所以就错了,这时候如果我们强行去更换位置,时间复杂度就变成了o(n^2)了,所以,这个思路被pass.

然后,我发现从前往后找不管怎样都逃不过位置变换的问题,然后我想从后往前找是不是会更好;然后我发现一个现象,最后一个位置的数的位置的直接确定的,即后往前找,只需要不断地去掉一个数字,然后再不断地去找数字就可以了;就相当于,我们每次需要从剩下地数中去寻找第a[i] + 1小地数,然后再将剩下的数字去掉找到的这个数,然后我直接想到用主席树去写,然后我想树状数组是不是也是直接用数组去记录每个数字的个数就可以了,但是由于树状数组只能找到从1 - x范围内有多少个数字,由于有着单调性,所以再使用一个二分,最后就将这道题目写了出来,时间复杂度是:O(n * (logn)^2),不会超时:

贴上代码一份:

#include <iostream>
#include <cstdio>

using namespace std;

const int maxn = 1e5 + 5;

int n; 
int a[maxn], b[maxn];

inline int lowbit(int x) { return x & -x; }
inline void add(int idx, int x) {
	for(; idx <= n; idx += lowbit(idx))
		b[idx] += x;
}
inline int query(int x) {
	int res = 0;
	while(x) {
		res += b[x];
		x -= lowbit(x);
	}
	
	return res;
}

inline int find(int x) {
	int l = 1, r = n;
	while(l < r) {
		int mid = (l + r) >> 1;
		if(query(mid) >= x) r = mid;
		else l = mid + 1;
	}
	
	return l;
}


int main(void) {
//	freopen("in.txt", "r", stdin);
	scanf("%d", &n);
	for(int i = 2; i <= n; i ++) scanf("%d", &a[i]);
	
	for(int i = 1;i <= n; i ++)
		add(i, 1); 
	
	for(int i = n; i >= 1; i --) {
		a[i] = find(a[i] + 1);
		add(a[i], -1);
	}
	
	for(int i = 1; i <= n; i ++)
		if(i == n) printf("%d", a[i]);
		else printf("%d\n", a[i]);	
	
	return 0;
} 
发布了179 篇原创文章 · 获赞 1 · 访问量 7578

猜你喜欢

转载自blog.csdn.net/weixin_42596275/article/details/104648609