JZOJ5848. 【省选模拟2018.8.24】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.

题解

与其有交集,且不被包含的,
那么出去这次区间,就是完全没有任何交集的,还有被这个区间包含的,
这样,求被包含的,用离线的方法,还是很简单,用树状数组维护。

code

#include <queue>
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <string.h>
#include <cmath>
#include <math.h>
#include <time.h>
#define ll long long
#define N 100003
#define M 103
#define db double
#define P putchar
#define G getchar
#define inf 998244353
#define pi 3.1415926535897932384626433832795
using namespace std;
char ch;
void read(int &n)
{
    n=0;
    ch=G();
    while((ch<'0' || ch>'9') && ch!='-')ch=G();
    ll w=1;
    if(ch=='-')w=-1,ch=G();
    while('0'<=ch && ch<='9')n=(n<<3)+(n<<1)+ch-'0',ch=G();
    n*=w;
}

int max(int a,int b){return a>b?a:b;}
int min(int a,int b){return a<b?a:b;}
ll abs(ll x){return x<0?-x:x;}
ll sqr(ll x){return x*x;}
void write(ll x){if(x>9) write(x/10);P(x%10+'0');}

struct node
{
    int l,r,s;
}t[N*80];

bool cmp(node a,node b)
{
    return a.r<b.r || (a.r==b.r && a.s<b.s);
}

int n,m,L,R,ans[N*30],tot;
int ls[N],rs[N],s[N];

int x_(int x){return x&(-x);}

int get(int x)
{
    int S=0;
    for(int i=x;i;i=i-x_(i))
        S=S+s[i];
    return S;
}

void ins(int x)
{
    for(int i=x;i<=n;i=i+x_(i))
        s[i]++;
}

void build(int l,int r)
{
    int mid;tot++;
    ls[t[tot].l=l]++;rs[t[tot].r=r]++;
    if(l==r)return;
    read(mid);
    build(l,mid);
    build(mid+1,r);
}

int main()
{
    freopen("strange.in","r",stdin);
    freopen("strange.out","w",stdout);

    read(n);read(m);tot=0;
    build(1,n);ans[0]=tot;
    for(int i=1;i<=n;i++)rs[i]=rs[i]+rs[i-1];
    for(int i=n;i;i--)ls[i]=ls[i]+ls[i+1];

    for(int i=1;i<=m;i++)
        tot++,read(t[tot].l),read(t[tot].r),t[tot].s=i,ans[i]=ans[0]-rs[t[tot].l-1]-ls[t[tot].r+1];

    sort(t+1,t+1+tot,cmp);

    for(int i=1;i<=tot;i++)
        if(t[i].s)
        {
            ans[t[i].s]=ans[t[i].s]-get(t[i].r)+get(t[i].l-1);
        }else ins(t[i].l);

    for(int i=1;i<=m;i++)
        write(ans[i]),P('\n');

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值