P3084 [USACO13OPEN] Photo G 分析
题目概述
题目链接:https://www.luogu.com.cn/problem/P3084。
有 \(n\) 个牛,拍了 \(m\) 张照片,拍了 \([a_i,b_i]\) 中的牛,现在牛群中有一些特殊的牛,而且每一张照片有且仅有一个特殊的牛。问最多有多少特殊的牛。
分析
一开始会想到跟区间扯上关系,但我们只需要将这个区间看作一个限制条件即可。
即:在 \([a_i,b_i]\) 之间的牛满足:
- 至少有一头特殊的牛
- 最多有一头特殊的牛
然后,因为我们把这个看作为条件那我们在条件之外直接 \(dp\)。
即设 \(f_i\) 表示前 \(i\) 头牛最多有多少特殊的牛。
我们不知道是不是要选择这头牛。
事实上可以直接设 \(f_i\) 表示第 \(i\) 头牛是特殊的牛,那么目前最多有多少特殊的牛?
显然:
\[f_i=\max f_j+1
\]
这个转移 \(j\) 是有条件的:
- 不在所有包含 \(i\) 的区间内(满足条件 \(2\))
- 与 \(i\) 之间不能有一个独立的区间不包含他们两个(满足条件 \(1\))。
对于一个区间 \([a_i,b_i]\),那么 \(b_i+1\) 及后面的点决策点一定会 \(\geq a_i\)(满足条件 \(1\)),那么对于 \([a_i,b_i]\) 的决策点一定会 \(<a_i\)。
我们发现前面那一个是好做的,赋值之后直接前缀 \(max\) 就行了。
后面的那一个可以你可以想象一下,我 \([1,a_i]\) 的决策点是不是也一定会 \(<a_i\) 啊!
那跟前面那一个一样直接维护前缀即可。
然后就有了 \(\mathcal{O}(n^2)\) 的代码,但是可以过(doge)。
#include <iostream>
#include <cstdio>
#include <stdlib.h>
#include <cstring>
#include <algorithm>
#include <vector>
#define int long long
#define N 200005
using namespace std;
int n,m;
int f[N],mn[N],mx[N];
signed main(){
cin >> n >> m;
for (int i = 0;i <= n + 1;i ++) mn[i] = i;
int l,r;
while(m --){
scanf("%lld%lld",&l,&r);
mn[r] = min(mn[r],l - 1);
mx[r + 1] = max(mx[r + 1],l);
}
for (int i = 1;i <= n + 1;i ++) mx[i] = max(mx[i - 1],mx[i]);
for (int i = n;i;i --) mn[i] = min(mn[i + 1],mn[i]);
for (int i = 1;i <= n + 1;i ++) {
l = mx[i],r = mn[i];
while(l <= r && l < i) f[i] = max(f[i],f[l ++] + 1);
if (f[i] == 0) f[i] = -1;
}
if (f[n + 1] > 1) f[n + 1] --;
cout << f[n + 1];
return 0;
}
考虑怎么变成 \(\mathcal{O}(n)\) 或者一个理论可行的复杂度。
注意到 \(l\) 是不断变大的, \(r\) 也是。
那不就成了一个滑动窗口吗?
拿下!
代码
时间复杂度 \(\mathcal{O}(n)\)。
#include <iostream>
#include <cstdio>
#include <stdlib.h>
#include <cstring>
#include <algorithm>
#include <vector>
#define int long long
#define N 200005
using namespace std;
int n,m;
int f[N],mn[N],mx[N],q[N];
signed main(){
cin >> n >> m;
for (int i = 0;i <= n + 1;i ++) mn[i] = i;
int l,r;
while(m --){
scanf("%lld%lld",&l,&r);
mn[r] = min(mn[r],l - 1);
mx[r + 1] = max(mx[r + 1],l);
}
for (int i = 1;i <= n + 1;i ++) mx[i] = max(mx[i - 1],mx[i]);
for (int i = n;i;i --) mn[i] = min(mn[i + 1],mn[i]);
int head = 1,tail = 0;
q[++tail] = 0;
for (int i = 1;i <= n + 1;i ++) {
// l = mx[i],r = mn[i];
// while(l <= r && l < i) f[i] = max(f[i],f[l ++] + 1);
// if (f[i] == 0) f[i] = -1;
l = mx[i],r = mn[i];
int j = q[tail] + 1;
for (;j <= r && j < i;j ++) {
while(head <= tail && f[q[tail]] < f[j]) tail --;
q[++tail] = j;
}
while(head <= tail && q[head] < l) head ++;
if (head > tail) f[i] = -1;
else f[i] = f[q[head]] + 1;
if (f[i] <= 0) f[i] = -1;
}
if (f[n + 1] > 0) f[n + 1] --;
cout << f[n + 1];
return 0;
}

浙公网安备 33010602011771号