【bzoj2811】APIO2012 守卫 guard

来源:互联网 发布:淘宝购物车删除 编辑:程序博客网 时间:2024/05/17 07:58

       题目,点击就送

           这道题竟然不是差分?!(虽然看着很像,但是注意他有一个K的限制,而且差分求出的只是一个合理的方案,也不满足这里的必须。。)       在网上搜到了一个说用差分A了的人,然而他说为了不带坏小朋友,就不说这种骗分的做法了(%%%)。

           标准解法是贪心,首先删除那些一定没有的区间,zro hzwer orz 是把区间重新编号,我懒得编,就每次去二分位置,然后去掉那些区间长度为1的区间,这是必定有的。对于剩下的,这里要判一下是否剩下的和可能点个数相等,若相等,则全都是啦。首先在区间右边端点贪心可以得到解,然后我们考虑由右端点向左走,如果这时算出来的答案已经大于了我点的限制,那么前面那个一定是一个解了,这里可以用动归处理,细节处理解释在代码里面有。。

           

#include<iostream>#include<cstdio>#include<algorithm>#include<cstdlib>#include<cstring>#include<queue>#define N 200005using 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[N],b[N];int c[N],res[N],sum[N],sum2[N],f[N],rp[N],la[N];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[N];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


2 0
原创粉丝点击