NOIP2016 提高二試 蚯蚓 題解

Coder.AN發表於2021-01-01

根據題意,每次都選取長度最長的蚯蚓將其切開,這不由的讓我們想到了大頂堆和優先佇列(其實優先佇列的實質也是堆),但這種做法的複雜度卻達到了O[M*log(N+M)],將題目最大資料規模代入約為1.4億,這顯然無法在1s的時間內通過。

那還有什麼更好的演算法能讓我們快速的在一組資料中找到一個最大值呢?

假設存在長度分別為a[ i ]和a[ j ]的蚯蚓,令其滿足i<j且a[ i ]>=a[ j ](可以認為a[]陣列是單調非遞增的)。

因為a[ i ]>=a[ j ]

所以a[ i ]*p>=a[ j ]*p (p>=0, p∈R)

所以int(a[ i ]*p)>=int(a[ j ]*p) (int()相當於下取整)

所以{ a[ i ] - int(a[ i ]*p) } >= { a[ j ] - int(a[ j ]*p }

也就是說,如果儲存待切蚯蚓長度的陣列a[]滿足單調非遞增,則其按題意產生的左右兩段蚯蚓的長度一定也能夠按被切的順序單調非遞增。分析到這裡,有沒有看出什麼貓膩呢?沒錯,我們可以用單調佇列解決!

我們開三個佇列(在這我就不用STL了,用陣列模擬)a[], x[], y[],分別儲存原始蚯蚓長度(即讀入的)、被切為左段的長度、被切為右段的長度。當然a[]是預先進行非遞減排序的。這樣,每1秒從三個佇列選取值最大的隊頭出隊,將其“切”成兩段後再分別從隊尾存入x[]和y[]。

 

程式碼如下:(max函式返回的是三個佇列中最大隊頭的值,同時對隊頭最大的對列進行出隊操作。)

 

const int MaxN = 100000+2;
const int MaxM = 7000000+2;

int a[MaxN], front_a, back_a;
int x[MaxN+MaxM], front_x, back_x;
int y[MaxN+MaxM], front_y, back_y;

int max(){
	int *front = NULL;
	int Max = 1<<31;
	if(front_a!=back_a && a[front_a]>Max){
		front = &front_a;
		Max = a[front_a];
	}
	if(front_x!=back_x && x[front_x]>Max){
		front = &front_x;
		Max = x[front_x];
	}
	if(front_y!=back_y && y[front_y]>Max){
		front = &front_y;
		Max = y[front_y];
	}
	++(*front);
	return Max;
}

最核心的問題解決了。現在還面臨一個問題:每1秒除剛剛產生的蚯蚓外,其餘長度全部增加q。如果按照題意來模擬,將其餘全部佇列中的元素掃描一遍並加q,單次操作的複雜度便達到了O(N+M),這是顯然吃不消的。

 

還有更優的辦法嗎?

反過來想:他讓我們把其餘元素加q,那我們不妨把新產生的兩個元素減q,這樣各個元素之間的相對值是保持不變的。但是最終所有元素的值與實際值也肯定存在一個相對值“Plus”,我們只要最後把他加回來就好了。

程式碼如下:

 

bool flag = false;
int Plus = 0;
int cnt = 0;
for(int m = 1; m<=M; ++m){
	now = max()+Plus;
		
	if(m%T==0 && cnt<M/T){
		flag ? printf(" ") : flag = true;
		printf("%d", now);
		++cnt;
	}
		
	_x = int(now*P);    //left
	_y = now-_x;        //right
		
	Plus += Q;
	
	x[back_x++] = _x-Plus;
	y[back_y++] = _y-Plus;
}
printf("\n");


最主要的兩個問題解決了,其他的估計也就順理成章了吧!下附總程式碼

 

 

#include <cstdio>
#include <algorithm>
using namespace std;

const int MaxN = 100000+2;
const int MaxM = 7000000+2;

int N, M, Q, U, V, T;
int _x, _y, Plus;
int cnt, now;
double P;
bool flag;

int a[MaxN], front_a, back_a;
int x[MaxN+MaxM], front_x, back_x;
int y[MaxN+MaxM], front_y, back_y;

inline bool cmp(int a, int b){
	return a>b;
}

int max(){
	int *front = NULL;
	int Max = 1<<31;
	if(front_a!=back_a && a[front_a]>Max){
		front = &front_a;
		Max = a[front_a];
	}
	if(front_x!=back_x && x[front_x]>Max){
		front = &front_x;
		Max = x[front_x];
	}
	if(front_y!=back_y && y[front_y]>Max){
		front = &front_y;
		Max = y[front_y];
	}
	++(*front);
	return Max;
}

int main() {
	freopen("earthworm.in", "r", stdin); //freopen("earthworm.out", "w", stdout);

	scanf("%d %d %d %d %d %d", &N, &M, &Q, &U, &V, &T);
	P = U/double(V);
	
	for(int i = 0; i<N; ++i) scanf("%d", &a[back_a++]);
	sort(a, a+N, cmp);
	
	for(int m = 1; m<=M; ++m){
		now = max()+Plus;
		
		if(m%T==0 && cnt<M/T){
			flag ? printf(" ") : flag = true;
			printf("%d", now);
			++cnt;
		}
		
		_x = int(now*P);
		_y = now-_x;
		
		Plus += Q;
		
		x[back_x++] = _x-Plus;
		y[back_y++] = _y-Plus;
	}
	printf("\n");
	
	flag = false;
	cnt = 0;
	
	for(int m = 1; cnt<(N+M)/T; ++m){
		now = max()+Plus;
		if(m%T==0){
			flag ? printf(" ") : flag = true;
			printf("%d", now);
			++cnt;
		}
	}
	printf("\n");
	fclose(stdin); fclose(stdout); return 0;
}

 

 

 

 

 

相關文章