[CF797F]Mice and Holes 题解

传送门QAQ

有一条数轴,上面有 \(n\) 个老鼠和 \(m\) 个洞,每个洞有一个容量限制。

每个老鼠要走到一个洞里,并且每个洞里的老鼠数量不能超过限制。

求老鼠走的总距离的最小值。

\(1\le n,m\le 5000\)

Analysis

很有意思的一道题。

明显的 DP,因为这道题的容量限制让人很难受,但是状态并不好设出来。

遇到这种情况,考虑从性质出发,让状态可以更好地表示出来。

一个非常非常显然的结论:老鼠的路线可能有重叠,但不可能交叉。

我自己推到这里感觉不太行就放弃了

根据这个结论,画一画就能发现,如果老鼠的路线没有任何重叠,那么每个洞里的老鼠在数轴上一定是连续的。

这个时候 DP 就变得简单了。

\(f(i,j)\) 表示前 \(i\) 个洞容纳了 \(j\) 个老鼠的最小答案。

\(f(i,j)=\max\limits_{k\in [j-limit_i,j]}\{f(i-1,k)+\sum\limits_{q=k+1}^j |x_q-x_i|\}\)

滑动窗口最大值,直接单调队列优化一下就好了。

时空复杂度 \(\mathcal O(nm)\)

Code

// Problem: Cf797f Mice and Holes
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/Cf797f
// Memory Limit: 250 MB
// Time Limit: 1500 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include <bits/stdc++.h>
#define pb emplace_back
#define fir first
#define sec second
typedef long long ll;
typedef std::pair<int,int> pii;

const int maxn = 5005;
int n,m,Q[maxn],head,tail,a[maxn];
pii s[maxn];
ll f[maxn][maxn],sum[maxn],v[maxn];

int main() {
	scanf("%d %d",&n,&m);
	for(int i = 1;i <= n;++ i)
		scanf("%d",&a[i]);
	std::sort(a + 1 , a + 1 + n);
	
	int tot = 0;
	for(int i = 1;i <= m;++ i) {
		scanf("%d %d",&s[i].fir,&s[i].sec);
		tot += s[i].sec;
	}
	std::sort(s + 1 , s + 1 + m);
	
	if(tot < n) {
		puts("-1");
		return 0;
	}
	
	memset(f , 0x3f , sizeof(f));
	f[0][0] = 0;
	for(int i = 1;i <= m;++ i) {
		head = tail = 1;
		Q[head] = Q[tail] = v[0] = v[1] = 0;
		f[i][0] = 0;
		for(int j = 1;j <= n;++ j) {
			sum[j] = sum[j - 1] + std::abs(s[i].fir - a[j]);
			for(;head <= tail&&Q[head] < j - s[i].sec;++ head)Q[head] = 0;
			for(;head <= tail&&v[tail] >= f[i - 1][j] - sum[j];-- tail)Q[tail] = v[tail] = 0;
			Q[++ tail] = j;
			v[tail] = f[i - 1][j] - sum[j];
			f[i][j] = v[head] + sum[j];
		}
	}
	
	printf("%lld\n",f[m][n]);
	return 0;
}
posted @ 2022-10-11 09:38  ImALAS  阅读(41)  评论(0编辑  收藏  举报