【JZOJ A组】Strange

Description

小 S 热爱大自然, 一天他种了一棵奇怪的线段树.
奇怪的线段树是一种与普通线段树类似的结构, 唯一不同的是, 它不一定以每一个区间的中点作为分治中心.
麻烦的是, 小 S 的线段树被风吹散了, 散成了一个个表示单一区间的结点, 而且正在逐渐飘远. 不过小 S 早有准备, 他可以进行抓取操作, 每一次他可以给出一个抓取区间, 由于这个抓取区间只有两个端点有磁力, 所以只能抓取满足与抓取区间有交而不被抓取区间包含的所有线段树结点.
现在小 S 进行了若干次抓取操作, 对于每次操作, 他希望你能回答他一共抓取了多少个线段树结点.

Input

第一行两个正整数 N, M 表示线段树的最大长度和抓取操作的次数. 接下来按照先序遍历顺序输入奇怪的线段树每个节点的信息: 如果当
前区间是叶子节点, 则返回, 否则输入一个 mid 表示当前区间 [l, r] 的两个
子区间分别是 [l, mid] 和 [mid + 1, r].
接下来 M 行, 每行两个正整数 L R 表示一次抓取操作.

Output

输出 M 行, 每行一个整数, 表示一次抓取操作的答案.

Sample Input

10 10
3
2
1
7
4
5
6
9
8
8 10
10 10
7 7
7 7
7 10
9 10
10 10
1 3
4 5
6 10

Sample Output

2
3
5
5
5
4
3
1
4
4

Data Constraint

对于 20% 的数据, N, M ≤ 5000.
对于 40% 的数据, N, M ≤ 100000.
对于另外 30% 的数据, L = 1.
对于 100% 的数据, N ≤ 100000, M ≤ 3000000.

思路

一开始想在线段树上跑LCA,结果发现好麻烦,于是我用了树状数组

维护两个树状数组,一个是询问lx < l的区间右端点,另一个维护未加入第一个集合的区间右端点
显然答案就是总数-(右端点 < lx的个数)- (未加入集合右端点 < r的个数)

代码

#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxm=4000077,maxn=200077;
struct E
{
    int l,r,d;
}e[maxm],q[maxm];
int cnt,n,m,p=1,tr2[maxn],tr1[maxn],k[maxn],ans[maxm];
bool cmp(E x,E y)
{
    return x.l<y.l;
}
void build(int l,int r)
{
    if (l==r) return;
    int mid;scanf("%d",&mid);
    e[++cnt].l=l,e[cnt].r=r;
    build(l,mid),build(mid+1,r);
}
int lowbit(int x)
{
    return x&-x;
}
void ins1(int x,int d)
{
    for(int i=x; i<=n; i+=lowbit(i)) tr1[i]+=d;
}
void ins2(int x,int d)
{
    for(int i=x; i<=n; i+=lowbit(i)) tr2[i]+=d; 
}
int query1(int x)
{
    int r=0;
    for (int i=x; i; i-=lowbit(i)) r+=tr1[i];
    return r;
}
int query2(int x)
{
    int r=0;
    for (int i=x; i; i-=lowbit(i)) r+=tr2[i];
    return r;
}
int main()
{
    freopen("strange.in","r",stdin); freopen("strange.out","w",stdout);
    scanf("%d%d",&n,&m);
    build(1,n);
    for(int i=1; i<=m; i++) scanf("%d%d",&q[i].l,&q[i].r),q[i].d=i;
    sort(q+1,q+m+1,cmp); sort(e+1,e+cnt+1,cmp);
    for(int i=1; i<=cnt; i++) 
    {
        k[e[i].l]++; ins2(e[i].r,1);
    }
    for(int i=n; i>=1; i--) k[i]+=k[i+1];
    for(int i=1; i<=m; i++)
    {
        while(e[p].l<q[i].l&&p<=cnt) 
        {
            ins1(e[p].r,1); ins2(e[p].r,-1); 
            p++;
        }
        ans[q[i].d]=cnt-query1(q[i].l-1)-query2(q[i].r)-k[q[i].r+1];
    }
    for(int i=1; i<=m; i++) printf("%d\n",ans[i]);
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值