非常神秘的题,使我的大脑旋转两年半
题意
给定正整数 \(N, L\) 和一个长度为 \(N\) 的正整数序列 \(A = (A_1, A_2, \dots, A_N)\)。
对于 \(i = 1, 2, \dots, N\),请回答以下问题:
是否存在一个长度为 \(L\) 的非负整数序列 \(B = (B_1, B_2, \dots, B_L)\),使得
\[\sum_{j=1}^{L-1} \sum_{k=j+1}^{L} |B_j - B_k| = A_i \]如果存在,请求出所有满足条件的 \(B\) 中 \(\max(B)\) 的最小值;如果不存在,请输出 \(-1\)。
- \(1 \leq N \leq 2 \times 10^5\)
- \(2 \leq L \leq 2 \times 10^5\)
- \(1 \leq A_i \leq 2 \times 10^5\)
- 输入均为整数
怎么做
这道题本来我发现了单调性,想着能不能二分,然后发现我就是个弱智。
既然没有想法,就先去找性质把。
我们发现 \(B\) 的顺序不会影响结果,而这个绝对值符号让我们束手束脚的。
我们故假设 \(B\) 是单调不降的。
为了方便,我们记 \(A[i]\) 为 \(val\),因为我不喜欢使用 \(k\)。
这样问题就成了 \(\sum_{i=1}^{L-1}\sum_{j=i+1}^{L}(B_j-B_i)\)。
我们又发现 \(B[1]\) 一定要是 \(0\) 才能使问题答案最优。
如果我们知道相邻两项差扩大或着减小的贡献就好了。
我们猛地发现了一个神奇的事情,那就是我们既然规定了 \(B\) 单调不降,那么我们的每一次计算实际上就可以被一大堆相邻项的差所表示。
比如说我们计算的 \(B_j-B_i\)。
这个就可以表示为 \((B_j-B_{j-1})+(B_{j-1}-B_{j-2})+......\) 像一个区间一样把其中的一些点加起来。
我们考虑一对相邻项会被贡献多少次。
我们发现这个次数是固定的,我们统计区间数量,也就是左端点可以存在的地方和右端点可以存在的地方乘起来。
最后我们可以得出这个问题可以变成 \(\sum_{i=1}^{L-1}i(L-i)(B_{i+1}-B_i)\)
这个我们就可以看作一个背包问题来进行考虑了。
重量是 \(i(L-i)\),价格是 \(1\)。
我们做完全背包就行了。
代码如下
点击查看代码
#include <bits/stdc++.h>
using namespace std;
namespace BaiBaiShaFeng{
const int MN=1e6+116;
int v[MN], w[MN], dp[MN];
void solve(int N, int L, int A[]){
memset(dp,0x3f,sizeof(dp)); dp[0]=0;
for(int j=1; j*(L-j)<MN&&j<L; ++j){
for(int i=j*(L-j); i<MN; ++i){
dp[i]=min(dp[i],dp[i-j*(L-j)]+1);
}
}
for(int i=1,val; i<=N; ++i){
if(dp[A[i]]==0x3f3f3f3f) cout<<-1<<'\n';
else cout<<dp[A[i]]<<'\n';
}
}
}
const int MN=1e6+116;
int n, l, a[MN];
signed main(){
ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
cin>>n>>l; for(int i=1; i<=n; ++i) cin>>a[i];
BaiBaiShaFeng::solve(n,l,a);
return 0;
}

浙公网安备 33010602011771号