1. 程式人生 > 其它 >【luogu AT5160】Numbers on a Circle(貪心)(堆)

【luogu AT5160】Numbers on a Circle(貪心)(堆)

給你一個長度為 n 的環,你每次操作可以把一個位置加上它兩邊的值。 然後給你初始狀態和目標狀態,問你至少要多少次操作才能實現,或者輸出無法實現。

Numbers on a Circle

題目連結:luogu AT5160

題目大意

給你一個長度為 n 的環,你每次操作可以把一個位置加上它兩邊的值。
然後給你初始狀態和目標狀態,問你至少要多少次操作才能實現,或者輸出無法實現。

思路

考慮反過來,變成你每次會減去兩邊的值。
那這個有什麼用呢,你會發現每次只會有一些點可以做。
(它一定要大於它兩邊的和)

而且它們之間一定有間隔,也就是說它們之間誰先做無關。
那我們就可以直接暴力用堆列舉它們,然後直接減。

那你一次一次減當然是不可以的,所以我們要直接減到不能減。
(或者減到目標,但如果減不到目標而且直接減會比目標小就無解)

最後判一下是否全部都減好了就可行了。

程式碼

#include<queue>
#include<cstdio>
#define ll long long

using namespace std;

int n, a[200001], b[200001];
priority_queue <pair<int, int> > q;
bool ok[200001];
ll ans;

bool up(int now) {
	if (ok[now]) return 0;
	return b[now] > b[(now + 1) % n] + b[(now - 1 + n) % n];
}

int main() {
	scanf("%d", &n);
	for (int i = 0; i < n; i++)
		scanf("%d", &a[i]);
	for (int i = 0; i < n; i++)
		scanf("%d", &b[i]);
	
	for (int i = 0; i < n; i++)
		if (up(i)) q.push(make_pair(b[i], i));
	
	while (!q.empty()) {
		int now = q.top().second;
		q.pop();
		
		int to = b[now] % (b[(now + 1) % n] + b[(now - 1 + n) % n]);
		if (to > a[now]) {
			ans += b[now] / (b[(now + 1) % n] + b[(now - 1 + n) % n]); b[now] = to;
			if (up((now + 1) % n)) q.push(make_pair(b[(now + 1) % n], (now + 1) % n));
			if (up((now - 1 + n) % n)) q.push(make_pair(b[(now - 1 + n) % n], (now - 1 + n) % n));
		}
		else {
			if ((b[now] - a[now]) % (b[(now + 1) % n] + b[(now - 1 + n) % n])) {
				printf("-1"); return 0;
			}
			ans += (b[now] - a[now]) / (b[(now + 1) % n] + b[(now - 1 + n) % n]);
			b[now] = a[now];
			if (up((now + 1) % n)) q.push(make_pair(b[(now + 1) % n], (now + 1) % n));
			if (up((now - 1 + n) % n)) q.push(make_pair(b[(now - 1 + n) % n], (now - 1 + n) % n));
			ok[now] = 1;
		}
	}
	
	for (int i = 0; i < n; i++)
		if (a[i] != b[i]) {
			printf("-1"); return 0;
		} 
	
	printf("%lld", ans);
	
	return 0;
}