1. 程式人生 > >ACM/ICPC 2018亞洲區預選賽北京賽站網路賽 D. 80 Days

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