【bzoj2811】APIO2012 守卫 guard
2016-04-14 10:29
225 查看
题目,点击就送
这道题竟然不是差分?!(虽然看着很像,但是注意他有一个K的限制,而且差分求出的只是一个合理的方案,也不满足这里的必须。。) 在网上搜到了一个说用差分A了的人,然而他说为了不带坏小朋友,就不说这种骗分的做法了(%%%)。标准解法是贪心,首先删除那些一定没有的区间,zro hzwer orz 是把区间重新编号,我懒得编,就每次去二分位置,然后去掉那些区间长度为1的区间,这是必定有的。对于剩下的,这里要判一下是否剩下的和可能点个数相等,若相等,则全都是啦。首先在区间右边端点贪心可以得到解,然后我们考虑由右端点向左走,如果这时算出来的答案已经大于了我点的限制,那么前面那个一定是一个解了,这里可以用动归处理,细节处理解释在代码里面有。。
#include<iostream> #include<cstdio> #include<algorithm> #include<cstdlib> #include<cstring> #include<queue> #define N 200005 using namespace std; int n,m,u,tot=0; struct eee{ int a,b,c; eee(int _a=0,int _b=0,int _c=0):a(_a),b(_b),c(_c){} bool operator<(const eee &o)const{ return a<o.a||(a==o.a&&b>o.b); } }a ,b ; int c ,res ,sum ,sum2 ,f ,rp ,la ; void add(int x,int v){ while(x<=n){ c[x]+=v; x+=(x&(-x)); } } int ask(int x){ int ret=0; while(x){ ret+=c[x]; x-=(x&(-x)); } return ret; } int ans ; int main(){ //freopen("","r",stdin); //freopen("","w",stdout); int i,j; scanf("%d%d%d",&n,&u,&m); for(i=1;i<=m;i++){ scanf("%d%d%d",&a[i].a,&a[i].b,&a[i].c); if(!a[i].c){add(a[i].a,1);add(a[i].b+1,-1);} } for(i=1;i<=n;i++){ if(ask(i))res[i]=0;//一定没有标记为0 else res[i]=1;//反之 sum[i]=sum[i-1]+res[i]; } for(i=1;i<=m;i++) if(a[i].c&&sum[a[i].b]==sum[a[i].a-1]+1){ int wei=upper_bound(sum+a[i].a,sum+a[i].b+1,sum[a[i].a-1])-sum; if(res[wei]!=2){//不确定是否一定有人 ans[++ans[0]]=wei; res[wei]=2; u--; } } else if(a[i].c&&!(sum[a[i].b]-sum[a[i].a-1])){puts("-1");return 0;} for(i=1;i<=n;i++){ if(res[i]==2)sum2[i]=sum2[i-1]+1; else sum2[i]=sum2[i-1]; } for(i=n;i>=1;i--){ if(res[i]==1)sum[i]=sum[i+1]+1; else sum[i]=sum[i+1]; } if(u==sum[1]&&u){ for(i=1;i<=n;i++) if(res[i])printf("%d\n",i); return 0; } else if(sum[1]<u){ puts("-1"); return 0; } // for(i=1;i<=ans[0];i++)cout<<"()()()()"<<ans[i]<<endl; sort(a+1,a+m+1); // for(i=1;i<=m;i++)cout<<a[i].a<<" "<< a[i].b<<" "<<a[i].c<<endl; for(i=1;i<=m;i++) if(a[i].c){ if(sum2[a[i].b]>sum2[a[i].a-1])continue;//此区间已满足, 不要了。 while(tot&&a[i].b<=b[tot].b)tot--;//若b把a完全包含,则不要b,因为若a满足了b一定满足 b[++tot]=a[i]; } // cout<<"***"<<tot<<endl; f[tot+1]=0; for(i=tot;i>=1;i--){ int ll=b[i].a,rr=b[i].b; int L=ll,R=rr; while(L<R){ int mid=(L+R+1)/2; if(sum[mid]-sum[rr+1]>0)L=mid; else R=mid-1; } rp[i]=L;//找到此区间真正的右端点 la[i]=upper_bound(b+i+1,b+tot+1,eee(rp[i],-1))-b;//找到左端点在我这里的那个 f[i]=f[la[i]]+1;//DP } int xz=1; while(xz<=tot){ int ll=b[xz].a,rr=b[xz].b; int L=ll,R=rr; while(L<R){ int mid=(L+R+1)/2; if(sum[mid]-sum[rr+1]>0)L=mid; else R=mid-1; } int wei=L;//找到真正的右端点 L=ll;R=rr; while(L<R){ int mid=(L+R+1)/2; if(sum[mid]-sum[rr+1]>1)L=mid; else R=mid-1; } int wei2=L;//找到真正的左端点,即wei~wei2中间必定有一个 j=upper_bound(b+xz+1,b+tot+1,eee(wei2,-1))-b;//找到左端点在wei2的区间 if(f[j]+1>u)ans[++ans[0]]=wei;//一定要放一个更优 u--; xz=upper_bound(b+xz+1,b+tot+1,eee(wei,-1))-b; } if(!ans[0])puts("-1"); else{ sort(ans+1,ans+ans[0]+1); for(i=1;i<=ans[0];i++)printf("%d\n",ans[i]); } //fclose(stdin); //fclose(stdout); return 0; }这题怎么会写这么长我去QAQ
相关文章推荐
- LeetCode 284. Peeking Iterator(支持peek的迭代器)
- UEFI引导系统
- 三维数据的展示
- 输入框填写提示
- Springboot文件下载
- ARC内存管理技术
- 开发常识 NO.1
- FJOI2016乱来记
- springmvc 静态资源处理
- 巩固C语言(二)
- Caused by: redis.clients.jedis.exceptions.JedisDataException: ERR Client sent AUTH, but no password
- NSURLSession和NSURLRequest
- Android APK反编译就这么简单 详解(附图)
- 设计模式 策略模式 以角色游戏为背景
- 小型数据库
- keil优化等级设置
- 《Linux内核分析》chapter4
- java指针
- 使用CStdioFile读写文件
- Beaglebone black开发板使用SPI1