NOIP提高组 被粉碎的线段树

Description

这里写图片描述

Data Constraint

这里写图片描述

Solution

我们发现区间定位个数(答案)和完全被该区间包含的节点个数所相关。

具体性质如下:区间定位个数(答案) = 2 * 区间长度 - 完全被该区间包含的节点个数。对于一个区间定位,它对答案的贡献为1,设它为区间[l..r],那么完全被该区间包含的节点个数为2* (r-l+1)-1。而该区间长度的两倍为2*(r-l+1),不难发现两者一减即为对答案的贡献1。于是我们可以将多个区将定位合并起来计算答案。

因此,我们将区间和询问分别按左指针从大到小进行排序,给区间一个指针j,对于每次询问,我们将区间的左指针大于询问中左指针的区间的右指针放进一个树状数组中。那么答案即为2 * 区间长度-树状数组中右指针比当前询问小的数量。总时间复杂度为O(N logN )。

代码

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=200006,maxn1=20;
struct code{
    int a,b,c;
}b[maxn],c[maxn];
int n,i,t,j,k,m,num,l;
int a[maxn],x,y,f[maxn],x1,ans[maxn];
void dg(int l,int r){
    b[++num].a=l,b[num].b=r;
    if (l==r) return;
    int t=++j;
    dg(l,a[t]);
    dg(a[t]+1,r);
}
bool cmp(code x,code y){
    return x.a<y.a;
}
int lowbit(int x){
    return x & (-x);
}
void insert(int x){
    if (x>n) return;
    f[x]++;
    insert(x+lowbit(x));
}
int find(int x){
    if (!x) return 0;
    return f[x]+find(x-lowbit(x));
}
int main(){
//  freopen("data.in","r",stdin);
    scanf("%d%d",&n,&m);
    for (i=1;i<n;i++)
        scanf("%d",&a[i]);j=0;
    dg(1,n);
    sort(b+1,b+num+1,cmp);
    for (i=1;i<=m;i++)
        scanf("%d%d",&c[i].a,&c[i].b),c[i].c=i;
    sort(c+1,c+m+1,cmp);j=num;
    for (i=m;i>=1;i--){
        while (b[j].a>=c[i].a && j) insert(b[j].b),j--;
        ans[c[i].c]=2*(1+c[i].b-c[i].a)-find(c[i].b);
    }
    for (i=1;i<=m;i++)
        printf("%d\n",ans[i]);
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值