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;
}

补一个更短的算法 尺取法

#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];

int main()
{
#ifdef LOCAL
	freopen("C:/input.txt", "r", stdin);
#endif
	int T;
	cin >> T;
	while (T--)
	{
		int n;
		ll c;
		cin >> n >> c;
		for (int i = 1; i <= n; i++)
			scanf("%lld", &a[i]);
		for (int i = n + 1; i <= n * 2; i++) //合并ab数组 并延长一倍
			scanf("%lld", &a[i]), a[i - n] -= a[i], a[i] = a[i - n];
		int l = 1, r = 1;
		while (l <= n && r - l + 1 <= n) //l循环一个n 区间长度大于n则满足条件
		{
			c += a[r++]; //加当前r 后移
			while (c < 0) //如果小于0则区间不满足 l后移
				c -= a[l++]; //并减去l
		}
		if (l <= n)
			cout << l << endl;
		else
			cout << -1 << endl;
	}

	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值