[CF797F]Mice and Holes 题解
有一条数轴,上面有 \(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;
}