在这个样例中,有两种可能的安排方式:1,3,5 或者 2,3,5。即 3 和 5
后面必然躲着一个忍者。
考虑第一个灌木丛,存在一种安排方案使得它的后面躲着忍者,但也存在一
种安排方案使得它后面没有躲忍者,因此不应该输出 1。同理,不应该输出 2。
题解:线段树+二分
先扫描所有的区间,用线段树的区间修改将所有监视区间中没有忍者的区间标记,表示这些点中不会有忍者。然后进行线段树的点查询,将所以可能存在忍者的点重新编号,使存在忍者的区间中的编号连续。
然后用o(n)的时间扫出每个点前面第一个有编号的点(即在线段树中没有标记)和后面第一个有编号的点(如果当前点有编号,那么就是自己)
如果剩下的点数等于忍者的个数,那么直接把所有剩下的点输出(注意这里需要特判)
把所有区间按左端点排序,如果一个区间直接包含了某个区间,那么这个区间直接舍去,就可以了(因为满足了小区间就一定满足了大区间)。所以现在的左端点和右端点都是单调的。说明一个问题必须点一定出现在区间右端点,因为右端点一定能属于更多的区间(也就是右端点是最优点,在右端点放能保证忍者数最少,如果最优点都不满足条件,那么一定无解了)。那么就是需要判断区间的右端点是否必须放。首先预处理出f[i]表示前i个区间的最少要放的忍者数(从左到右没有忍者的区间在右端点放一个),g[i]表示后i个区间的最少要放的忍者数。我们就去判断当前区间的右端点x的前一个x-1这个点能否放,放x-1是需要二分一个最大的ans1,b[i].r<x-1,一个最小的ans2,使b[i].l>x-1,f[ans1]+g[ans2]+1即为当前的忍者需要量,如果大于k,说明R这个位置如果不放,就一定无法只用k个忍者来满足这些限制要求,所以R是必须的,输出其原始位置。
#include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #include<cmath> #define N 200003 using namespace std; struct data { int l,r,p,mark; };data ask[N],a[N],b[N]; int n,m,k,delta[N*4],tr[N*4],cnt,s[N],tot; int pre[N],next[N],f[N],g[N],pos[N],st[N]; void pushdown(int x) { if (!delta[x]) return; delta[x<<1]=delta[x<<1|1]=1; tr[x<<1]=tr[x<<1|1]=1; delta[x]=0; } void qjchange(int now,int l,int r,int ll,int rr,int v) { if (l>=ll&&r<=rr) { delta[now]=1; tr[now]=1; return ; } int mid=(l+r)/2; if (ll<=mid) qjchange(now<<1,l,mid,ll,rr,v); if (rr>mid) qjchange(now<<1|1,mid+1,r,ll,rr,v); } bool query(int now,int l,int r,int x) { if (l==r) return tr[now]; int mid=(l+r)/2; pushdown(now); if(x<=mid) return query(now<<1,l,mid,x); else return query(now<<1|1,mid+1,r,x); } void point() { for (int i=1;i<=n;i++) if (!query(1,1,n,i)) s[i]=++tot,pos[tot]=i; } int cmp(data a,data b) { return a.l<b.l||a.l==b.l&&a.r>b.r; } int main() { scanf("%d%d%d",&n,&k,&m); for (int i=1;i<=m;i++) { scanf("%d%d%d",&ask[i].l,&ask[i].r,&ask[i].p); if (!ask[i].p) qjchange(1,1,n,ask[i].l,ask[i].r,1); } point(); if (tot==k) { for (int i=1;i<=n;i++) if (s[i]) printf("%d\n",i); return 0; } int t=0; for (int i=1;i<=n;i++) { if (s[i]) t=i; pre[i]=t; } t=n+1; for (int i=n;i>=1;i--) { if (s[i]) t=i; next[i]=t; } for (int i=1;i<=m;i++) if (ask[i].p) { cnt++,a[cnt].l=s[next[ask[i].l]],a[cnt].r=s[pre[ask[i].r]]; if (a[cnt].l>a[cnt].r) cnt--; } sort(a+1,a+cnt+1,cmp); int top=1; st[top]=1; for (int i=2;i<=cnt;i++) { while (a[i].r<=a[st[top]].r&&top) top--; st[++top]=i; } for (int i=1;i<=top;i++) b[i].l=a[st[i]].l,b[i].r=a[st[i]].r; int mx=0,mn=tot+1; for (int i=1;i<=top;i++) if (b[i].l>mx) f[i]=f[i-1]+1,mx=b[i].r; else f[i]=f[i-1]; for (int i=top;i;i--) if (b[i].r<mn) g[i]=g[i+1]+1,mn=b[i].l; else g[i]=g[i+1]; f[0]=g[0]=0; bool flag=false; for (int i=1;i<=top;i++) { if (f[i]!=f[i-1]+1) continue; if (b[i].l==b[i].r) { flag=true; printf("%d\n",pos[b[i].l]); continue; } int x=b[i].r-1; int l=1; int r=i-1; int ans1=0,ans2=0; while (l<=r) { int mid=(l+r)/2; if (b[mid].r<x) ans1=mid,l=mid+1; else r=mid-1; } l=i+1; r=top; while (l<=r) { int mid=(l+r)/2; if (b[mid].l>x) ans2=mid,r=mid-1; else l=mid+1; } if (f[ans1]+g[ans2]+1>k) { flag=1; printf("%d\n",pos[b[i].r]); } } if (!flag) printf("-1\n"); return 0; }