守卫
一道大难题
原著
好处理的是0区间,把他们删掉,然后重标号。对于0区间在\(L\)数组中把它并到右边,在\(R\)数组中把它并到左边。这样的作用是对于1区间,如果它其中包含部分0区间,就可以去掉这些0区间。(说不太清楚,手推一下样例)
然后对剩下的1区间处理一下(重标号了嘛),排序,去掉包含了小区间的大区间(它没用)。然后借助贪心。。。。
看代码吧(好像也不是特别理解)
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define mid ((l+r)>>1)
using namespace std;
const int N = 100005;
struct node{
int l,r,f;
bool operator < (const node &cc){
return l<cc.l;
}
}a[N];
int b[N],L[N],R[N],be[N],F[N],G[N];
int read()
{
char c=getchar();
int ans=0,w=1;
while((c>'9'||c<'0')&&c!='-') c=getchar();
if(c=='-') { w=-1; c=getchar(); }
while(c>='0'&&c<='9')
{ ans=ans*10+c-'0'; c=getchar(); }
return ans*w;
}
//bool cmp(node x,node y)
//{ return x.l<y.l;}
int main()
{
int n=read(),k=read(),m=read();
for(int i=1;i<=m;i++)
{
a[i].l=read(); a[i].r=read(); a[i].f=read();
if(a[i].f==0) ++b[a[i].l],--b[a[i].r+1];
}
int cur=0,cnt=0;
for(int i=1;i<=n;i++)
{
cur+=b[i];
if(cur==0) L[i]=R[i]=++cnt,be[cnt]=i;
}
if(cnt==k)
{
for(int i=1;i<=cnt;i++)
printf("%d\n",be[i]);
return 0;
}
L[n+1]=n+1;
for(int i=1;i<=n;i++)
if(R[i]==0) R[i]=R[i-1];
for(int i=n;i>=1;--i)
if(L[i]==0) L[i]=L[i+1];
cnt=0;
for(int i=1;i<=m;i++)
{
if(a[i].f==0) continue;
int l=L[a[i].l],r=R[a[i].r];
if(l<=r) a[++cnt].l=l,a[cnt].r=r;
}
sort(a+1,a+cnt+1);
int top=0;
for(int i=1;i<=cnt;i++)
{
while(top&&a[i].l>=L[top]&&a[i].r<=R[top]) --top;
L[++top]=a[i].l,R[top]=a[i].r;
}
int l=n+1,r=0;
for(int i=1;i<=top;i++)
{
if(L[i]>r) F[i]=F[i-1]+1,r=R[i];
else F[i]=F[i-1];
}
for(int i=top;i>=1;i--)
{
if(R[i]<l) G[i]=G[i+1]+1,l=L[i];
else G[i]=G[i+1];
}
bool ok=0;
for(int i=1;i<=top;i++)
{
if(F[i]==F[i-1]) continue;
if(L[i]==R[i])
{
printf("%d\n",be[R[i]]);
ok=1; continue;
}
int l=1,r=i-1,x=0,y=top+1;
while(l<=r)
{
if(R[mid]<R[i]-1) x=mid,l=mid+1;
else r=mid-1;
}
l=i+1,r=top;
while(l<=r)
{
if(L[mid]>R[i]-1) y=mid,r=mid-1;
else l=mid+1;
}
if(F[x]+G[y]+1>k)
{
printf("%d\n",be[R[i]]);
ok=1;
}
}
if(!ok) puts("-1");
return 0;
}