【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;
} 
posted @ 2022-03-22 20:09  あおいSakura  阅读(65)  评论(0)    收藏  举报