首先想到能不能用差分搞搞,但是给自己绕进去了 /kel
我们不妨给 \(\{b_L\}\) 定个不降的序(如果打在数轴上,显然序和答案无关),于是可以拿掉绝对值。
注意到这个和式(记其结果为 \(x\) )中每个 \(b_i\) 的贡献系数 \(c_i = 2i - L - 1\),于是有:
直接做不好处理最大值。\(\{b_n\}\) 有序,想到 Abel 变换(记 \(b_0=0,s_i = \sum_{j = i}^{L} c_j,d_i = b_i - b_{i-1}\)
):
答案等价于最小化 \(\sum d_i\)。
显然 \(d_i\ge0\),且可以证明(打表) \(s_i \ge 0\),于是就可以把上面的式子看做背包的过程,跑一遍背包即可。
#include <bits/stdc++.h>
#define inf 0x3f3f3f3f
#define Linf 0x3f3f3f3f3f3f3f3f
#define pii pair<int, int>
#define all(v) v.begin(), v.end()
#define int long long
using namespace std;
//#define filename "xxx"
#define FileOperations() freopen(filename".in", "r", stdin), freopen(filename".out", "w", stdout)
//#define multi_cases 1
namespace Traveller {
const int N = 2e5+2;
int n, L;
int sum[N], f[N];
void main() {
cin >> n >> L;
for(int i = L; i >= 1; --i) sum[i] = sum[i+1] + 2*i - L - 1;
memset(f, 0x3f, sizeof(f));
f[0] = 0;
for(int i = 1; i <= L; ++i)
for(int j = sum[i]; j <= 2e5; ++j) //完全背包
f[j] = min(f[j], f[j - sum[i]] + 1);
for(int i = 1, a; i <= n; ++i) {
scanf("%lld ", &a);
printf("%lld\n", f[a] > 1e9 ? -1 : f[a]);
}
}
}
signed main() {
#ifdef filename
FileOperations();
#endif
signed _ = 1;
#ifdef multi_cases
scanf("%d", &_);
#endif
while(_--) Traveller::main();
return 0;
}
关于时间复杂度:
记值域为 \(V\)。
我的代码中双重循环看起来什么没加,是 \(O(VL)\) 的,但是注意到 sum
数组增速很快,是平方级别的,也就是说他会在 \(O(\sqrt{V})\) 次就超出 \(2 \times 10^5\),不进行二重循环。
于是复杂度就是 \(O(V \sqrt{V})\),可能带点常数。