[Apio2009]CONVENTION会议中心 解题报告

这题并不会做。。
这道题的主要问题是如何求一个区间的最大不相交线段覆盖,思路是可以用倍增来加速贪心。就是说我们本来是求一个区间里最多有多少条线段,我们把它转换成二分/倍增线段数,求其最短的区间是多少。
代码:

#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!想请变量的意义。

你可能感兴趣的:(线段树,贪心,倍增)