题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5372

题意:给你n次操作,每次增加线段或者删除第i次增加操作中增加的线段,问你每次增加操作中,所增加的线段会覆盖多少条完整的线段。

解法:对于新插入的线段,查询有多少个线段左端点大于等于该线段的左端点。 再查询有多少个线段的右端点大于该线段右端点, 两者之差就是答案。用两个树状数组搞定。时间复杂度nlogn


#include <bits/stdc++.h>
using namespace std;
const int maxn=4e5+10;
int a[maxn],b[maxn],op[maxn],c[2][maxn];
vector<int>v;
int getid(int x){
    return lower_bound(v.begin(),v.end(),x)-v.begin()+1;
}
inline lowbit(int x){
    return x&-x;
}
void add(int x, int v, int o){
    while(x<maxn){
        c[o][x]+=v;
        x+=lowbit(x);
    }
}
int query(int x, int o){
    int ret=0;
    while(x){
        ret += c[o][x];
        x-=lowbit(x);
    }
    return ret;
}

int main()
{
    int n,ks=0;
    while(~scanf("%d", &n))
    {
        int clk=0;
        for(int i=1; i<=n; i++){
            scanf("%d %d", &op[i], &a[i]);
            if(op[i]==0){
                b[i]=a[i]+(++clk);
                v.push_back(a[i]);
                v.push_back(b[i]);
            }
        }
        sort(v.begin(),v.end());
        v.erase(unique(v.begin(),v.end()),v.end());
        memset(c,0,sizeof(c));
        clk = 1;
        printf("Case #%d:\n", ++ks);
        for(int i=1; i<=n; i++){
            if(op[i]==0){
                int l=getid(a[i]),r=getid(b[i]);
                printf("%d\n", query(r,1)-query(l-1,0));
                a[clk]=l;
                b[clk++]=r;
                add(l,1,0);
                add(r,1,1);
            }
            else{
                add(a[a[i]], -1, 0);
                add(b[a[i]], -1, 1);
            }
        }
    }
    return 0;
}