bzoj2811 apio2012 守卫 guard 贪心
2016-04-13 23:49
197 查看
题目意思很好理解(为什么我会想到查分约束QAQ) ,正解居然是贪心(apio和noi/noip系列确实还是赶脚不一样)
首先要做的是把一定没有忍者的区间删除。用线段树操作比较方便,之后给每一个没被删除位置(从1---n)一个新的编号,
为了将读入的区间重新表示出来(即要使读入的L,R都应该在没被删的地方),假设区间为(L,R),如果L所处被删,自然应该去找L右边第一个没被删的,R则为左边。两次扫描就可以得到,注意不要那些已经被彻底删完了的区间。
转回来想问题,其实就是类似于区间覆盖,会有一些灌木丛很优,能同时满足多个要求,那么大体思路是如果假设不选一个灌木丛,而其灌木丛最少需要的忍者数加起来超过了 k 的限制,那么这个被假设的灌木丛一定得选。
用区间覆盖来解决这个问题,当把区间按左端点排序后,去掉具有包含关系的区间中大的那一个(满足了小区间就一定满足大区间),(这样所有区间左右端点都单增了)每次在一个区间的右端点放一个忍者,(这样能保证使用忍者数量最少),多余的忍者随便放即可满足,这里可以看到一个性质,就是只有区间的右端点才有可能成为必须点(理解 多余的随便放),那么现在要做的就是判断一个区间的右端点(R)是否必须放。令F[i]表示i及其之前的位置最少放多少个忍者,G[i]表示i及其之后的位置最少放多少个忍者,我们就去判断R-1这个点能否放(R-1比R-2优),放R-1是需要二分一个最大的k1,f[k1].R
<R-1,一个最小的k2,使f[k2].L>R-1,F[k1]+G[k2]+1即为当前的忍者需要量,如果大于k,说明R这个位置如果不放,就一定无法只用k个忍者来满足这些限制要求,所以R是必须的,输出其原始位置。
最后,要注意如果一开始删完区间后发现灌木丛数一共只有k个了,直接输出就好。注意-1的输出不要忘了。代码如下。
首先要做的是把一定没有忍者的区间删除。用线段树操作比较方便,之后给每一个没被删除位置(从1---n)一个新的编号,
为了将读入的区间重新表示出来(即要使读入的L,R都应该在没被删的地方),假设区间为(L,R),如果L所处被删,自然应该去找L右边第一个没被删的,R则为左边。两次扫描就可以得到,注意不要那些已经被彻底删完了的区间。
转回来想问题,其实就是类似于区间覆盖,会有一些灌木丛很优,能同时满足多个要求,那么大体思路是如果假设不选一个灌木丛,而其灌木丛最少需要的忍者数加起来超过了 k 的限制,那么这个被假设的灌木丛一定得选。
用区间覆盖来解决这个问题,当把区间按左端点排序后,去掉具有包含关系的区间中大的那一个(满足了小区间就一定满足大区间),(这样所有区间左右端点都单增了)每次在一个区间的右端点放一个忍者,(这样能保证使用忍者数量最少),多余的忍者随便放即可满足,这里可以看到一个性质,就是只有区间的右端点才有可能成为必须点(理解 多余的随便放),那么现在要做的就是判断一个区间的右端点(R)是否必须放。令F[i]表示i及其之前的位置最少放多少个忍者,G[i]表示i及其之后的位置最少放多少个忍者,我们就去判断R-1这个点能否放(R-1比R-2优),放R-1是需要二分一个最大的k1,f[k1].R
<R-1,一个最小的k2,使f[k2].L>R-1,F[k1]+G[k2]+1即为当前的忍者需要量,如果大于k,说明R这个位置如果不放,就一定无法只用k个忍者来满足这些限制要求,所以R是必须的,输出其原始位置。
最后,要注意如果一开始删完区间后发现灌木丛数一共只有k个了,直接输出就好。注意-1的输出不要忘了。代码如下。
#include<iostream> #include<set> #include<cmath> #include<queue> #include<cstdio> #include<bitset> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; typedef long long LL; const int N=400005,INF=0x7fffffff/2; struct Data{int L,R,k;}a ,q ,f ; int n,m,k,jud ,Ls ,Rs ,cnt=0,To ,num=0,top=0; int F ,G ; void Sub(int v,int L,int R,int x,int y) { int mid=(L+R)>>1; if(x<=L&&R<=y){jud[v]=1;return;} if(x<=mid)Sub(2*v,L,mid,x,y); if(y>mid)Sub(2*v+1,mid+1,R,x,y); } bool Ask(int v,int L,int R,int pos) { if(jud[v])return 1; int mid=(L+R)>>1; if(L==R)return 0; if(pos<=mid)return Ask(2*v,L,mid,pos); return Ask(2*v+1,mid+1,R,pos); } bool cmp(const Data x,const Data y){return x.L<y.L||(x.L==y.L&&x.R>y.R);} int main() { scanf("%d%d%d",&n,&k,&m); int x,y,i,j; for(i=1;i<=m;i++) { scanf("%d%d%d",&a[i].L,&a[i].R,&a[i].k); if(!a[i].k)Sub(1,1,n,a[i].L,a[i].R); } for(i=1;i<=n;i++) if(!Ask(1,1,n,i))Ls[i]=Rs[i]=++cnt,To[cnt]=i; if(cnt==k) { for(i=1;i<=cnt;i++)printf("%d\n",To[i]); return 0; } Ls[0]=0; for(i=1;i<=n;i++)if(!Ls[i])Ls[i]=Ls[i-1]; Rs[n+1]=INF; for(i=n;i>=1;i--)if(!Rs[i])Rs[i]=Rs[i+1]; for(i=1,num=0;i<=m;i++) if(a[i].k) { x=Rs[a[i].L];y=Ls[a[i].R]; if(x<=y)q[++num]=(Data){x,y,0}; } sort(q+1,q+num+1,cmp); for(i=1,top=0;i<=num;i++) { while(top>=1&&f[top].L<=q[i].L&&q[i].R<=f[top].R)top--; f[++top]=q[i]; } int Max=0,Min=INF; for(i=1;i<=top;i++) if(f[i].L>Max)F[i]=F[i-1]+1,Max=f[i].R; else F[i]=F[i-1]; for(i=top;i>=1;i--) if(f[i].R<Min)G[i]=G[i+1]+1,Min=f[i].L; else G[i]=G[i+1]; bool SPE=0; int L,R,mid,k1,k2; for(i=1;i<=top;i++) { if(F[i]!=F[i-1]+1)continue; if(f[i].L==f[i].R){SPE=1;printf("%d\n",To[f[i].L]);continue;} x=f[i].R-1; k1=0;k2=top+1; L=1;R=i-1; while(L<=R) { mid=(L+R)>>1; if(f[mid].R<x)k1=mid,L=mid+1; else R=mid-1; } L=i+1;R=top; while(L<=R) { mid=(L+R)>>1; if(f[mid].L>x)k2=mid,R=mid-1; else L=mid+1; } if(F[k1]+G[k2]+1>k){SPE=1;printf("%d\n",To[f[i].R]);} } if(!SPE)printf("-1"); return 0; }
相关文章推荐
- dwr3实现消息精确推送详细步骤
- 二分查找-lintcode
- 1.1.1 Linux是什么
- Android Studio 引用jar和 so 文件
- 理解TimesTen中两个CheckPoint文件与Trans Log的关系及其原理
- 21_锁屏和恢复出厂设置
- ReactNative学习十八-Text/Image属性与风格
- WAMP集成环境
- 《Linux内核分析》课程第八周学习总结
- 二分搜索的java源码实践
- 20_读取播放音效
- 中介者模式
- TimesTen内存数据库体系结构
- 19_读取短信内容
- Android自定义控件2-简单的写字板控件
- 18_开机广播
- 17_使用设备管理器
- for循环的小技巧
- intellij idea蓝屏重启后代码到处报错解决方法
- OSChina 周四乱弹 ——霍金!你要真挑起来星球大战,放学你等着!