agc037_c Numbers on a Circle

题目叙述

Link

给出一个填满数字的环,求最少变换多少次能变换为另一个环。

每次可以进行如下变换:

  • 将一个数换为左边一个和右边有个数还有自己的和。

题解

考虑倒着做。下面 a[i] 为原始环,b[i] 为最终环

原操作相当于 a[i]+=a[i-1]+a[i+1] ,所以倒着做相当于 b[i]-=b[i-1]+b[i+1] ,也就是减掉相邻两个数。

那么考虑 b 的最大与对应位置 a 不相等的数,如果不进行 b[i]-=b[i-1]+b[i+1] ,那么他两边的数必然不能改变,所以他也不能改变。所以必然他先改变。变到恰好大于等于 a[i] 即可。但如果恰好比 a[i] 大的时候仍然比 b[i-1]+b[i+1] 大,那么无解。

所以使用堆维护所有没有变成正确数值的位置即可。

代码

#include <cstdio>
#include <queue>
#define int long long
using namespace std;
const int NC = 2e5 + 5;
int N, A[NC], B[NC];
int pre(int i) { return (i == 1) ? N : (i - 1); }
int nxt(int i) { return (i == N) ? 1 : (i + 1); }
struct cmp {
	bool operator()(const int &x, const int &y) { return B[x] < B[y]; }
};
priority_queue<int, vector<int>, cmp> H;
signed main() {
	freopen("circle.in", "r", stdin);
	scanf("%lld", &N);
	for (int i = 1; i <= N; ++i) scanf("%lld", &A[i]);
	for (int i = 1; i <= N; ++i) scanf("%lld", &B[i]);
	for (int i = 1; i <= N; ++i) if (A[i] != B[i]) H.push(i);
	int ans = 0;
	while (H.size()) {
		int tp = H.top();
		H.pop();
		int dec = (B[tp] - A[tp]) / (B[pre(tp)] + B[nxt(tp)]);
		if (dec == 0 && B[tp] != A[tp]) {
			printf("-1
");
			return 0;
		}
		B[tp] -= dec * (B[pre(tp)] + B[nxt(tp)]);
		ans += dec;
		if (B[tp] != A[tp])
			H.push(tp);
	}
	printf("%lld
", ans);
	fclose(stdin);
	return 0;
}
//这也要开 long long
原文地址:https://www.cnblogs.com/YouthRhythms/p/13544062.html