题目描述
样例
input:
3 6 3
1 1
3 2
4 3
0 1 2 7 1 2
1 2 0 3 4 5
7 9 5 0 4 6
output:
4
PS: 本题需快读,若采用出题人给的快读,需要从文件读入,测试的时候要freopen,但是提交的时候不用。
分析
由于对于每个吵闹的人,可以算出从
i
i
i开始睡觉的情况下,这个吵闹的人需要被调到那个区间开始吵。设其他量与题目描述一致,
l
e
n
len
len表示吵闹的人可以开始吵闹的区间
S
S
S的大小,则易得:
l
e
n
=
m
−
k
−
b
i
+
1
len=m-k-b_i+1
len=m−k−bi+1
可以看到,
l
e
n
len
len的大小是固定的,因此可以看做是一个滑动窗口。
接下来可以枚举开始睡觉的时间 j j j,那么对于这个时间,可以通过查询 S S S中的 c i , j c_{i,j} ci,j的最小值来算得从 j j j开始睡觉所需要的最小代价,其基础是由于每个吵闹的人之间是相互独立的。
PS: 可以从前一天睡到第二天,也可以从前一天吵到后一天,因此是一个环,需要倍长展开。
那么最后的问题是怎么求区间最小值呐?我考场上想到线段树,但是显然常数巨大的
O
(
n
m
l
o
g
m
)
O(nmlogm)
O(nmlogm)会
T
T
T掉最后二十分。如果用
S
T
ST
ST,比如某大佬就是,那么很容易
M
L
E
MLE
MLE而爆零。
所以,最后留下一个办法,区间滑动求
R
M
Q
RMQ
RMQ可以用单调队列线性解。
单调队列参见这里。
代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
namespace io {
const int L = (1 << 21) + 1;
char ibuf[L], *iS, *iT, obuf[L], *oS = obuf, *oT = obuf + L - 1, c, st[55];
int f, tp;
#define gc() (iS==iT?(iT=(iS=ibuf)+fread(ibuf,1,L,stdin),(iS==iT?EOF:*iS++)):*iS++)
inline void gi(int& x) {
for (f = 1, c = gc(); c < '0' || c > '9'; c = gc())
if (c == '-') f = -1;
for (x = 0; c <= '9' && c >= '0'; c = gc()) x = x * 10 + (c & 15);
x *= f;
}
};
using io::gi;
const int MAXN=5010;
int n,m,k;
int a[MAXN],b[MAXN];
int c[MAXN][MAXN],mn[MAXN][MAXN];
deque<int> q;
int main()
{
gi(n);gi(m);gi(k);
for(int i=1;i<=n;i++){
gi(a[i]);gi(b[i]);
if(k+b[i]>m) return !puts("-1");//若S区间大于m,则不可能
}memset(c,0x3f,sizeof(c));
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++) gi(c[i][j]);
for(int j=m+1;j<=m*2;j++) c[i][j]=c[i][j-m];//扩充一倍
}memset(mn,0x3f,sizeof(mn));
for(int i=1;i<=n;i++){
int len=m-k-b[i]+1;
while(!q.empty()) q.pop_back();
q.push_back(0);
for(int j=1;j<=2*m;j++){
while(!q.empty()&&j-q.front()>=len) q.pop_front();
while(!q.empty()&&c[i][q.back()]>=c[i][j])
q.pop_back();q.push_back(j);
mn[i][j]=min(mn[i][j],c[i][q.front()]);
}
}//单调队列
//mn[i][j]指第i个人从j往前len区间内的最小的代价
int ans=5e7+10;
for(int i=1;i<=m;i++){
int now=0;
for(int j=1;j<=n;j++){
int ed=i-b[j];
if(ed<=m-k-b[j]+1) ed+=m;//由于环的原因,在len以前的mn数据不能反映最小代价
//因此需要向前移m位来纠正
now+=mn[j][ed];
}ans=min(now,ans);
}printf("%d\n",ans);
}