非常神秘的题,使我的大脑旋转两年半

题意

给定正整数 \(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;
}
posted @ 2025-08-31 21:30  BaiBaiShaFeng  阅读(13)  评论(0)    收藏  举报
Sakana Widget右下角定位