2811: [Apio2012]Guard
Time Limit: 10 Sec Memory Limit: 128 MBSubmit: 673 Solved: 303
[ Submit][ Status][ Discuss]
Description
Input
Output
Sample Input
5 3 4
1 2 1
3 4 1
4 4 0
4 5 1
1 2 1
3 4 1
4 4 0
4 5 1
Sample Output
3
5
5
HINT
在这个样例中,有两种可能的安排方式:1,3,5 或者 2,3,5。即 3 和 5
后面必然躲着一个忍者。
考虑第一个灌木丛,存在一种安排方案使得它的后面躲着忍者,但也存在一
种安排方案使得它后面没有躲忍者,因此不应该输出 1。同理,不应该输出 2。
Source
对于每一个剩下的区间,我们预处理从左往右到这个区间覆盖所需最少忍者数,已经最后一个忍者放在哪里,同理从右往左试一遍(这时候就是优先放左端点了),这个用贪心瞎搞一下
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn = 1E5 + 10;
const int INF = ~0U>>1;
int n,k,m,cnt,tot,ans,L[maxn],R[maxn],s1[maxn],Pre[maxn],s2[maxn],Min[maxn]
,Nex[maxn],Num[maxn],po[maxn],Max[maxn],tl[maxn],tr[maxn],Ans[maxn];
int getint()
{
char ch = getchar();
int ret = 0;
while (ch < '0' || '9' < ch) ch = getchar();
while ('0' <= ch && ch <= '9')
ret = ret*10 + ch - '0',ch = getchar();
return ret;
}
int main()
{
#ifdef DMC
freopen("DMC.txt","r",stdin);
#endif
n = getint();
k = getint();
m = getint();
for (int i = 1; i <= m; i++) {
int l,r,typ;
l = getint();
r = getint();
typ = getint();
if (!typ) ++s1[l],--s1[r+1];
else {
L[++cnt] = l;
R[cnt] = r;
++s2[l];
--s2[r+1];
}
}
int rest = 0;
for (int i = 1; i <= n; i++) {
bool flag1,flag2;
flag1 = flag2 = 0;
s1[i] += s1[i-1];
if (!s1[i]) flag1 = 1,++rest;
s2[i] += s2[i-1];
if (s2[i]) flag2 = 1;
if (flag1 && flag2) {
Num[i] = ++tot;
po[tot] = i;
}
}
if (rest == k) {
for (int i = 1; i <= n; i++)
if (!s1[i])
printf("%d\n",i);
return 0;
}
for (int i = 1; i <= n; i++)
Pre[i] = Num[i]?i:Pre[i-1];
for (int i = n; i; i--)
Nex[i] = Num[i]?i:Nex[i+1];
for (int i = 1; i <= tot; i++) Min[i] = INF;
for (int i = 1; i <= cnt; i++) {
L[i] = Num[Nex[L[i]]];
R[i] = Num[Pre[R[i]]];
Min[L[i]] = min(Min[L[i]],R[i]);
}
cnt = 0;
for (int i = 1; i <= tot; i++) {
if (Min[i] == INF) continue;
while (cnt && R[cnt] >= Min[i]) --cnt;
L[++cnt] = i;
R[cnt] = Min[i];
}
Max[0] = -1;
for (int i = 1; i <= cnt; i++)
if (L[i] <= Max[i-1]) {
tl[i] = tl[i-1];
Max[i] = Max[i-1];
}
else {
tl[i] = tl[i-1] + 1;
Max[i] = R[i];
}
Min[cnt+1] = INF;
for (int i = cnt; i; i--)
if (R[i] >= Min[i+1]) {
tr[i] = tr[i+1];
Min[i] = Min[i+1];
}
else {
tr[i] = tr[i+1] + 1;
Min[i] = L[i];
}
for (int i = 1; i <= cnt; i++) {
if (L[i] == R[i]) {
Ans[++ans] = po[R[i]];
continue;
}
int sl,sr,l,r,tmp = R[i] - 1,A,B;
l = 0; r = cnt + 1;
while (r - l > 1) {
int mid = (l + r) >> 1;
if (Max[mid] >= tmp) r = mid;
else l = mid;
}
if (Max[r] < tmp) sl = tl[r],A = r;
else sl = tl[l],A = l;
l = 0; r = cnt + 1;
while (r - l > 1) {
int mid = (l + r) >> 1;
if (Min[mid] <= tmp) l = mid;
else r = mid;
}
if (Min[l] > tmp) sr = tr[l],B = l;
else sr = tr[r],B = r;
if (B == i) ++B;
int Add = A >= B - 1?0:1;
if (sl + sr + Add > k)
Ans[++ans] = po[R[i]];
}
if (!ans) {
cout << -1;
return 0;
}
sort(Ans + 1,Ans + ans + 1);
for (int i = 1; i <= ans; i++)
printf("%d\n",Ans[i]);
return 0;
}
对于每一个剩下的区间,我们预处理从左往右到这个区间覆盖所需最少忍者数,已经最后一个忍者放在哪里,同理从右往左试一遍(这时候就是优先放左端点了),这个用贪心瞎搞一下