题目大意:
题目链接:https://www.luogu.org/problem/P3084
一个长度为
n
n
n的序列,给出
m
m
m个二元组
(
l
,
r
)
(l,r)
(l,r),表示
[
l
,
r
]
[l,r]
[l,r]中有且仅有一个数字1。求该01序列中最多含有多少个1。
思路:
60 p t s 60pts 60pts
题型很明显是差分约束。
对于其中一个二元组
(
l
,
r
)
(l,r)
(l,r),我们需要满足的是
s
[
r
]
−
s
[
l
−
1
]
=
1
s[r]-s[l-1]=1
s[r]−s[l−1]=1,所以就变成
s
[
r
]
≤
s
[
l
−
1
]
+
1
,
s
[
r
]
≥
s
[
l
−
1
]
+
1
s[r]\leq s[l-1]+1,s[r]\geq s[l-1]+1
s[r]≤s[l−1]+1,s[r]≥s[l−1]+1。
然后显然任意一个数字只能选或不选,所以有
s
[
i
]
−
s
[
i
−
1
]
≤
1
,
s
[
i
]
−
s
[
i
−
1
]
≥
0
s[i]-s[i-1]\leq 1,s[i]-s[i-1]\geq0
s[i]−s[i−1]≤1,s[i]−s[i−1]≥0,变一下型就可以了。
但是这样会被卡。。。只能拿到60分。
代码链接
100 p t s 100pts 100pts
我们设
f
[
i
]
f[i]
f[i]表示在位置
i
i
i必放1,前
i
i
i个数字中1最多的个数。
那么既然在位置
i
i
i选择了1,那么所有含有位置
i
i
i的区间都不可以选1。
设
r
[
i
]
=
m
i
n
(
l
x
∣
i
∈
(
l
x
,
r
x
)
)
r[i]=min(l_x|i\in (l_x,r_x))
r[i]=min(lx∣i∈(lx,rx))。这样的话,
r
[
i
]
∼
i
−
1
r[i]\sim i-1
r[i]∼i−1都不可以选择1。
但是因为每个区间至少要有一个1,所以不含
i
i
i的区间都必须要有1。
设
l
[
i
]
=
m
a
x
(
l
x
∣
l
x
<
i
)
l[i]=max(l_x|l_x<i)
l[i]=max(lx∣lx<i),那么在
l
x
l_x
lx的右区间内必然会至少有一个1。
所以位置
i
i
i的上一个1的区间范围就是
[
l
[
i
]
,
r
[
i
]
]
[l[i],r[i]]
[l[i],r[i]]。
由于要求最大,就用单调队列维护一下就可以了。
代码:
#include <queue>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N=200010;
int n,m,f[N],l[N],r[N];
deque<int> q;
int main()
{
scanf("%d%d",&n,&m);
for (int i=1;i<=n+1;i++)
r[i]=i-1;
for (int i=1,x,y;i<=m;i++)
{
scanf("%d%d",&x,&y);
l[y+1]=max(l[y+1],x);
r[y]=min(r[y],x-1);
}
for (int i=2;i<=n+1;i++)
l[i]=max(l[i-1],l[i]);
for (int i=n;i>=1;i--)
r[i]=min(r[i],r[i+1]);
int j=1;
q.push_back(0);
for (int i=1;i<=n+1;i++)
{
while (q.size()&&q.front()<l[i]) q.pop_front();
for (;j<=r[i]&&j<=n+1;j++)
{
if (j<l[i] || f[j]<0) continue;
while (q.size()&&f[q.back()]<f[j]) q.pop_back();
q.push_back(j);
}
if (q.size()) f[i]=f[q.front()]+1;
else f[i]=-1;
}
if (f[n+1]>0) printf("%d\n",f[n+1]-1);
else printf("-1");
return 0;
}
本文解析洛谷P3084题目,介绍了一种使用差分约束和单调队列优化的解题策略,通过设定f[i]表示在位置i必放1时1的最大个数,利用r[i]和l[i]确定不可选1的区间,最终通过单调队列维护求得最优解。

被折叠的 条评论
为什么被折叠?



