ACM/ICPC 2018亚洲区预选赛北京赛站网络赛 D. 80 Days

题解

题目大意 n个点组成一个环形 初始钱为m 从i走到j需要-b[i] + a[j] 要求按照顺时针走完所有的点(不用再回到起点) 过程中m不能小于0 输出最小的起点编号

直接把a[i]和b[i]合在一起 看作到达这个点会增加的值 起点先算一次 做一次前缀和并且增长一倍记为s i枚举1到n为起点 用ST表求i到i+n-1区间的最小值-s[i - 1]+m如果大于等于0则满足条件

AC代码


#include <stdio.h>
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int INF = 0x3f3f3f3f;
const int MAXN = 2e6 + 10;
ll a[MAXN];
ll pw2[30], lg2[MAXN], mn[30][MAXN];

void getst(int n) //nlogn初始化st表 不可修改
{
	for (int i = 1; i <= n; i++)
		mn[0][i] = a[i]; //i到i+2^0-1就一个位置 最值等于自己本身
	for (int i = 1; i <= lg2[n]; i++)
		for (int j = 1; j + pw2[i] - 1 <= n; j++) //区间末尾不超过n
			mn[i][j] = min(mn[i - 1][j], mn[i - 1][j + pw2[i - 1]]); //max/min
}
ll ask(int l, int r)
{
	int w = lg2[r - l + 1]; //x >= 2^lg2(x) > x/2 从两端长度为pw2[x]一定会覆盖整个区间而又不会超出区间
	return min(mn[w][l], mn[w][r - pw2[w] + 1]); //max/min
}
int main()
{
#ifdef LOCAL
	freopen("C:/input.txt", "r", stdin);
#endif
	pw2[0] = 1, lg2[0] = -1;
	for (int i = 1; i < 30; i++)
		pw2[i] = pw2[i - 1] << 1;
	for (int i = 1; i < MAXN; i++)
		lg2[i] = lg2[i >> 1] + 1;
	int T;
	cin >> T;
	while (T--)
	{
		int n, m;
		cin >> n >> m;
		for (int i = 1; i <= n; i++)
		{
			int t;
			scanf("%d", &t);
			a[i] = t;
		}
		for (int i = 1; i <= n; i++)
		{
			int t;
			scanf("%d", &t);
			a[i] -= t;
		}
		for (int i = 1; i <= n; i++)
			a[i + n] = a[i];
		for (int i = 1; i <= n * 2; i++)
			a[i] += a[i - 1];
		getst(n * 2);
		int ans = INF;
		for (int i = 1; i <= n; i++) 
		{
			ll res = ask(i, i + n - 1);
			res = res - a[i - 1] + m;
			if (res >= 0)
			{
				ans = i;
				break;
			}
		}
		if (ans != INF)
			cout << ans << endl;
		else
			cout << -1 << endl;
	}

	return 0;
}

猜你喜欢

转载自blog.csdn.net/CaprYang/article/details/82814137