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;
} 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章