1. 程式人生 > 其它 >【luogu CF889E】Mod Mod Mod(性質)(DP)

【luogu CF889E】Mod Mod Mod(性質)(DP)

Mod Mod Mod

題目連結:luogu CF889E

題目大意

\(f(x,n)=1\)
\(f(x,i)=(x\mod a_i)+f(x\mod a_i,i+1)\)
然後給你陣列 \(a\),要你找到一個 x 使得 \(f(x,1)\) 最大,輸出這個 \(f(x,1)\) 即可。

思路

首先有一個很神奇的東西就是一定會有一個數組中的數 \(a_i\) 到它的貢獻是 \(a_i-1\)(否則你可以把這個數變大 \(1\),所以位置的貢獻都 \(+1\)

然後設 \(f_{i,j}\) 為前 \(i\) 個數組搞定了,然後當前是 \(j\) 的取模漏了的貢獻(也就是一個位置貢獻是 \(i*j+f_{i,j}\)

然後有一個東西是 \(f_{i,j}=\max\{f_{i,k}\}(k\geqslant j)\)

那有用的狀態似乎就只有 \(n^2\) 個了。
(就小於的位置不用,模了有變化的就模)
(或者讓這個位置作為 \(a_i-1\) 的位置,然後你就在大於的時候列舉一下,讓 \(f_{i,j}\) 作為做的值)

但其實是 \(n\log n\) 個狀態的,因為你取模每個數最多取 \(\log\) 個就沒了。
然後用 \(map\) 記錄當前狀態即可。

程式碼

#include<map>
#include<cstdio>
#include<algorithm>
#define ll long long

using namespace std;

const int N = 2e5 + 100;
ll n, x;
map <ll, ll> f;

int main() {
	scanf("%lld", &n);
	for (int i = 1; i <= n; i++) {
		scanf("%lld", &x);
		if (i == 1) f[x - 1] = 0;
			else {
				for (map <ll, ll> ::iterator it = f.lower_bound(x); it != f.end(); f.erase(it++)) {
					ll now = (*it).first, val = (*it).second;
					f[now % x] = max(f[now % x], val + (i - 1) * (now - now % x));
					f[x - 1] = max(f[x - 1], val + (i - 1) * (((now + 1) / x * x - 1) - (x - 1)));
				}
			}
	}
	
	ll ans = 0;
	for (map <ll, ll> ::iterator it = f.begin(); it != f.end(); it++)
		ans = max(ans, n * (*it).first + (*it).second);
	printf("%lld", ans);
	
	return 0;
}