您的位置:首页 > 其它

【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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: