JZOJ3542冒泡排序

題面

下面是一段實現冒泡排序算法的C++代碼:

for (int i=1;i<=n-1;i++) 
    for (int j=1;j<=n-i ;j++) 
        if(a[j]>a[j+1]) 
            swap(a[j],a[j+1]);

其中待排序的a數組是一個1~n的排列,swap函數將交換數組中對應位置的值。

對於給定的數組a以及給定的非負整數k,使用這段代碼執行了正好k次swap操作之後數組a中元素的值會是什麼樣的呢?

n<=1e6  k<=1e12

題解

  這題要開long long  

這是道結論題,

在嘗試了各種方法之後,我們嘗試直接構造答案數組。 
考慮每個數在答案數組裏的位置。 
定義j掃一遍爲一輪。 
分析發現:

1:每個數,每輪最多向左移動一個位置。

繼續:

2:若一個數左邊有比它大的數,它就一定會左移一個位置。 
2逆:若它左移一個位置,則它的左邊一定有數比它大。

再繼續:

3:設前面有t個數比它大,那麼前t輪,每一輪它都會向左移一個位置,然後它就再不左移。

思路似乎已經清晰。 
設經過x輪移動,在過不到一輪就交換k次。 
先來考慮經過剛好x輪移動的情況。

如果t<=x,那麼它的位置在哪裏? 
這種情況就是,一個數左移到一半就停止不移動了。 
顯然它的位置是可求的。 
如果t>x,那麼它的位置又在哪裏? 
這種數不會再左移,也就說明它的左邊沒有比它大的數。 
推出結論:

4:這部分數的位置一定是隨着值而遞增的。

我們現在已經知道一部分數的具體位置了,把剩下的數按從小到大的順序填到空位置裏就好了。

現在已經知道了x輪移動的答案,還剩下一點交換次數,就直接來一輪冒泡排序就好了。

5:發現:交換次數=左移次數。

根據結論2的逆結論,統計每一輪的左移次數就好了。

                                                                                                                             - by  我們都是星塵

那麼,如果我們知道k次交換後一共完整地進行了x輪的話,就可以把“ 

for(int i=1; i<n; i++)

” 這重循環給省掉,而直接O(n)計算殘缺的第二重循環,既然已經知道x輪移動的答案,而這個答案肯定是單調遞增的,就可以通過二分枚舉x。

CODE(加freopen)

#include<cstdio>
#include<iostream>
#include<cstring>
#include<vector>
#include<queue>
#include<stack>
#include<algorithm>
#define LL long long
#define MAXN 1000005
#define lowbit(x) (((-x) & (x)))
using namespace std;
inline LL read() {
	LL f = 1,x = 0;char s = getchar();
	while(s < '0' || s > '9') {if(s == '-') f = -1;s = getchar();}
	while(s >= '0' && s <= '9') {x = x * 10 + s - '0';s = getchar();}
	return x * f;
}
LL n,m,i,j,s,o,k,cnt;
LL a[MAXN],b[MAXN];
LL c[MAXN];
LL f[MAXN];
priority_queue<int> q;
void addtree(int x,LL y) {
	for(int i = x;i <= n;i += lowbit(i)) c[i] += y;return ;
}
inline LL sum(int x) {
	LL ans = 0;
	for(int i = x;i > 0;i -= lowbit(i)) ans += c[i];
	return ans;
}
inline LL check(LL a) {
	LL ans = 0;
	for(int i = 1;i <= n;i ++) {
		ans += min(a,f[i]);
	}
	return ans;
}
LL solve(LL l,LL r) {
//	printf("%d %d\n",l,r);
	if(l >= r - 1) {
		if(check(r) <= m) return r;
		return l;
	}
	LL mid = (l + r) / 2;
	if(check(mid) <= m) return solve(mid,r);
	return solve(l,mid);
}
int main() {
	freopen("sort.in","r",stdin);
	freopen("sort.out","w",stdout);
	n = read();m = read();
	for(int i = 1;i <= n;i ++) {
		a[i] = read();
		addtree(a[i],1);
		f[i] = sum(n) - sum(a[i]);
	}
//	for(int i = 1;i <= n;i ++) printf("s:%d ",sum(i));putchar('\n');
	LL aa = solve(0,n);
	m -= check(aa);
	for(int i = 1;i <= n;i ++) {
		if(f[i] >= aa) {
			b[i - aa] = a[i];
		}
		else {
			q.push(a[i]);
		}
	}
	for(int i = n;i > 0;i --) {
		if(!b[i]) {
			b[i] = q.top();q.pop();
		}
	}
//	for(int i = 1;i <= n;i ++) {
//		printf("%d ",b[i]);
//	}putchar('\n');
//	printf("%d\n",m);
	for(int i = 1;i < n;i ++) {
		if(!m) break;
		if(b[i] > b[i + 1]) {
			swap(b[i],b[i + 1]);
			m --;
		}
	}
	if(m > 0) {
		printf("Impossible!\n");
	}
	else {
		for(int i = 1;i <= n;i ++) {
			printf("%d ",b[i]);
		}putchar('\n');
	}
	return 0;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章