Educational Codeforces Round 167 (Rated for Div. 2) D(dp,貪心)

基地AI發表於2024-06-28

比賽地址

前言

這場比較容易,但是D沒在考場上做出來。考場上一直在找規律求解 \(f_i\) 陣列,確沒去想這是一個dp。第二天早上突然發現這題是dp。www。

A,B,C 太水了就不提了。

D

把這個問題簡化為:給你 \(n\) 對關係,每對關係形如 \((a_i, a_i-b_i)\),意思是如果你現在手上的值 \(val\)\(val \ge a_i\) 那麼可以使得 \(val -= a_i-b_i\) 從而收穫 2 的貢獻。((使用條件,步長))然後會給你 \(m\) 個初始值 \(c_j\),求你可以獲得的最大貢獻。(每個初始值相互獨立)

資料範圍:\(1 \le a_i,b_i \le 10^6\)\(1 \le c_j \le 10^9\)

由這個資料範圍的特性容易想到先預處理 \(\max\{a_i\}\) 以內的,對於 \(c_j>\max\{a_i\}\) 的,用貪心走最短的步長(獲得最大的貢獻)走到 \(<\max\{a_i\}\)

那麼設 \(f_i\) 表示初始值為 \(i\) 可以走的最大貢獻。將 \(n\) 對關係放入一個桶陣列,下標表示使用條件(\(a_i\),存最短的步長(\(\min\{a_i-b_i\}\)(最有可能使得貢獻值最大),在狀態轉移前同時更新當前可用最短步長(\(s\),那麼狀態轉移方程為:

\(f[i] = f[i-s]+1\)

最後統計答案,對於每個 \(c_j\):如果 \(c_j \le \max\{a_i\}\),那麼直接累計 \(f[c_j]\) 進入答案;如果 \(c_j > \max\{a_i\}\),先減去若干倍(\(cnt\))全域性最小步長,得到一個最大的 \(k\),使得 \(k \le \max\{a_i\}\),那麼對答案的貢獻即 \(2cnt+f[k]\)

時間複雜度 \(O(n)\)

Talk is cheap.Show me the code.

#include<bits/stdc++.h>
#define int long long
using namespace std;
inline int read() {
    int x = 0, f = 1; char ch = getchar();
    while(ch<'0' || ch>'9') { if(ch=='-') f=-1; ch=getchar(); }
    while(ch>='0'&&ch<='9') { x=(x<<3)+(x<<1)+(ch^48); ch=getchar(); }
    return x * f;
}
const int N = 1e6+7;
int n,m;
int a[N],b[N],f[N];
int t[N];
signed main()
{
	n = read(), m = read();
	int mx = 0;
	for(int i=1;i<=n;++i) a[i] = read(), mx = max(mx, a[i]);
	for(int i=1;i<=n;++i) b[i] = read();
	for(int i=1;i<=n;++i) {
		t[a[i]] = (!t[a[i]] ? a[i]-b[i] : min(t[a[i]], a[i]-b[i]));
	}
	int step = 0;
	for(int i=1;i<=mx;++i) {
		if(t[i]) step = (!step ? t[i] : min(step, t[i]));
		if(step) f[i] = f[i-step] + 1;
	}
	int ans = 0;
	while(m--) {
		int c = read();
		if(c > mx) {
			int d = step;
			int cnt = (c-mx-1)/d + 1;
			ans += cnt + f[c-cnt*d];
		} else {
			ans += f[c];
		}
	}
	printf("%lld\n",ans*2);
	return 0;
}

相關文章