原題傳送門
先升序排序
令
d
p
i
dp_i
dpi表示包含這一個的答案
d
p
i
=
m
i
n
(
d
p
j
+
m
a
x
(
k
,
a
i
−
a
j
+
1
+
1
)
)
(
j
<
i
)
dp_i=min(dp_j+max(k,a_i-a_{j+1}+1))(j<i)
dpi=min(dpj+max(k,ai−aj+1+1))(j<i)
然後,求方案
如果
d
p
i
−
d
p
i
−
1
=
a
i
−
a
i
−
1
dp_i-dp_{i-1}=a_i-a_{i-1}
dpi−dpi−1=ai−ai−1,說明
i
i
i和
i
−
1
i-1
i−1屬於同一個區間
所以可以令
i
i
i屬於的區間右端點為
a
i
a_i
ai,左端點是
a
i
−
k
+
1
a_i-k+1
ai−k+1
否則
i
i
i和
i
−
1
i-1
i−1不屬於同一區間,那就構造
l
=
a
i
,
r
=
a
i
+
k
−
1
l=a_i,r=a_i+k-1
l=ai,r=ai+k−1是最優的
需要判斷的是如果
l
<
1
或
r
>
n
l<1或r>n
l<1或r>n需要區間整體移動一下
但是求
d
p
dp
dp值的部分是
O
(
m
2
)
O(m^2)
O(m2)
需要優化一下
討論
然後就優化到 O ( n ) O(n) O(n)了
Code:
#include <bits/stdc++.h>
#define maxn 300010
using namespace std;
struct data{
int val, id, l, r;
}a[maxn];
int n, m, k, dp[maxn], q[maxn];
inline int read(){
int s = 0, w = 1;
char c = getchar();
for (; !isdigit(c); c = getchar()) if (c == '-') w = -1;
for (; isdigit(c); c = getchar()) s = (s << 1) + (s << 3) + (c ^ 48);
return s * w;
}
bool cmp(data x, data y){ return x.val < y.val; }
bool cmp2(data x, data y){ return x.id < y.id; }
int main(){
n = read(), k = read();
m = read();
for (int i = 1; i <= m; ++i) a[i].val = read(), a[i].id = i;
sort(a + 1, a + 1 + m, cmp);
int p = 0, h = 1, t = 0, s = 1e9;
for (int i = 1; i <= m; ++i){
dp[i] = 1e9;
if (a[i].val - a[1].val + 1 <= k){
dp[i] = k, a[i].l = a[1].val, a[i].r = a[1].val + k - 1;
continue;
}
while (p < i && a[i].val - a[p + 1].val + 1 >= k) s = min(s, dp[p] - a[++p].val);
dp[i] = min(dp[i], s + a[i].val + 1);
if (a[i].val - a[p + 1].val + 1 < k && p < i) dp[i] = min(dp[i], dp[p] + k);
if (dp[i] - dp[i - 1] == a[i].val - a[i - 1].val)
a[i].r = a[i].val, a[i].l = a[i].val - k + 1;
else a[i].l = a[i].val, a[i].r = a[i].l + k - 1;
if (a[i].l < 1) a[i].r += 1 - a[i].l, a[i].l = 1;
if (a[i].r > n) a[i].l -= a[i].r - n, a[i].r = n;
}
printf("%d\n", dp[m]);
sort(a + 1, a + 1 + m, cmp2);
for (int i = 1; i <= m; ++i) printf("%d %d\n", a[i].l, a[i].r);
return 0;
}