这题并不会做。。
这道题的主要问题是如何求一个区间的最大不相交线段覆盖,思路是可以用倍增来加速贪心。就是说我们本来是求一个区间里最多有多少条线段,我们把它转换成二分/倍增线段数,求其最短的区间是多少。
代码:
#include<cstdio>
#include<iostream>
using namespace std;
#include<algorithm>
#include<cstring>
#include<cmath>
const int N=2e5+5;
int f[N<<1][18];
pair<int,int> range[N];
int hash[N<<1];
bool segt[N<<3];
#define lson node<<1,l,l+r>>1
#define rson node<<1|1,(l+r>>1)+1,r
void paint(int node){
segt[node]=1;
}
void pushup(int node){
segt[node]=segt[node<<1]|segt[node<<1|1];
}
void update(int node,int l,int r,int L,int R){
if(L<=l&&r<=R)paint(node);
if(l==r)return;
if(L<=l+r>>1)update(lson,L,R);
if(R>l+r>>1)update(rson,L,R);
pushup(node);
}
bool query(int node,int l,int r,int L,int R){
if(L<=l&&r<=R)return segt[node];
if(l>R||r<L)return 0;
return query(lson,L,R)|query(rson,L,R);
}
int lquery(int node,int l,int r,int x){
int tnode,tl,tr;
while(l!=r)
if(l+r>>1<x){
if(segt[node<<1])tnode=node<<1,tl=l,tr=l+r>>1;
node=node<<1|1,l=(l+r>>1)+1;
}
else node<<=1,r=l+r>>1;
node=tnode,l=tl,r=tr;
while(l!=r)
if(segt[node<<1|1])node=node<<1|1,l=(l+r>>1)+1;
else node<<=1,r=l+r>>1;
return l;
}
int rquery(int node,int l,int r,int x){
//printf("rquery(%d)\n",x);
int tnode,tl,tr;
while(l!=r){
//printf("%d[%d,%d]\n",node,l,r);
if(l+r>>1>=x){
if(segt[node<<1|1])tnode=node<<1|1,tl=(l+r>>1)+1,tr=r;
node<<=1,r=l+r>>1;
}
else node=node<<1|1,l=(l+r>>1)+1;
}
node=tnode,l=tl,r=tr;
//printf("%d[%d,%d]\n",node,l,r);
while(l!=r)
if(segt[node<<1])node<<=1,r=l+r>>1;
else node=node<<1|1,l=(l+r>>1)+1;
return r;
}
int query(int l,int r){
int ans=0;
for(int j=18;j--;)
if(f[l][j]<=r){
l=f[l][j]+1;
ans|=1<<j;
}
return ans;
}
int main(){
freopen("bzoj_1178.in","r",stdin);
freopen("bzoj_1178.out","w",stdout);
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%d%d",&range[i].first,&range[i].second);
hash[(i<<1)-1]=range[i].first,hash[i<<1]=range[i].second;
}
sort(hash+1,hash+(n<<1|1));
int htot=unique(hash+1,hash+(n<<1|1))-hash;
for(int i=htot;--i;)f[i][0]=htot;
for(int i=n;i;--i){
range[i].first=lower_bound(hash+1,hash+htot,range[i].first)-hash;
range[i].second=lower_bound(hash+1,hash+htot,range[i].second)-hash;
f[range[i].first][0]=min(f[range[i].first][0],range[i].second);
}
for(int i=18;i--;)f[htot+1][i]=f[htot][i]=htot;
for(int i=htot;--i;){
f[i][0]=min(f[i][0],f[i+1][0]);
for(int j=1;j<18;++j)f[i][j]=f[f[i][j-1]+1][j-1];
}
update(1,0,htot,0,0),update(1,0,htot,htot,htot);
int ans=query(1,htot-1);
printf("%d\n",ans);
int l,r;
for(int i=1;i<=n;++i){
//printf("---%d---\n",i);
if(!query(1,0,htot,range[i].first,range[i].second)){
l=lquery(1,0,htot,range[i].first)+1;
r=rquery(1,0,htot,range[i].second)-1;
if(query(l,range[i].first-1)+query(range[i].second+1,r)+1==query(l,r)){
update(1,0,htot,range[i].first,range[i].second);
printf("%d",i);
if(ans--)putchar(' ');
//puts("");
}
}
}
}
总结:
①对于有单调性的两个量,题目中确定一个求另一个时,我们往往将其转换成二分/倍增另一个求这个。
②注意序列长度在N前面的系数!并不总是1.
③线段树别忘了pushdown!想请变量的意义。