2019牛客暑期多校第八场 Inner World

在这里插入图片描述

题意

一开始n棵树,每棵树只有一个根节点,标号为1。多次操作,每次在区间 [ l , r ] [l,r] [l,r] 的树中的结点 u u u 下增加一个结点 v v v ,多次询问,问区间 [ l , r ] [l,r] [l,r] 的树的结点 u u u 的子树的大小求和

题解

肯定不能模拟建树了
题目说增加的结点 v v v 是互不相同的,所以可以建成一棵树,在结点加一个区间,表示在 [ l , r ] [l,r] [l,r] 中才存在这一个结点
考虑询问操作
相当于问 结点 u u u 的子树中,所有结点表示的区间与 [ l , r ] [l,r] [l,r] 求交集之后的区间大小,再求和
需要的是子树的操作,所以用 d f s dfs dfs 序,变成一段区间内,每个结点与 [ l , r ] [l,r] [l,r]的交集
发现这是一个二维空间求矩阵面积的问题
d f s dfs dfs 序是一维,区间 [ l , r ] [l,r] [l,r] 是另一维
最朴素的做法是一个二维树状数组,根据容斥加加减减就好了
但是空间不允许
考虑将一个矩形分成两部分求解
在这里插入图片描述
变为 ( 1 , y 1 ) − ( x 2 , y 2 ) (1,y_1)-(x_2,y_2) (1,y1)(x2,y2) 减去 ( 1 , y 1 ) − ( x 1 , y 2 ) (1,y_1)-(x_1,y_2) (1,y1)(x1,y2)
我们根据 d f s dfs dfs 序增加的顺序,实际上就是 x 坐标增加的顺序,每次区间修改是直接在之前的基础上修改,然后统计当前这个时间结点需要计算的答案
区间修改,区间求和,树状数组求可以了

代码
#include<bits/stdc++.h>
#define N 300010
#define LL long long
#define pb push_back
#define sc(x) scanf("%d",&x)
#define scc(x,y) scanf("%d%d",&x,&y)    
#define sccc(x,y,z) scanf("%d%d%d",&x,&y,&z)
using namespace std;

int l[N],r[N],in[N],out[N],id[N],cnt;LL ans[N];
vector<int>a[N];
struct EX_BIT{
    struct BIT{
        LL c[N];
        void update(int x,LL k){for(;x<N;c[x]+=k,x+=x&-x);}
        LL sum(int x){LL ans=0;for(;x>0;ans+=c[x],x-=x&-x);return ans;}
    }BIT1,BIT2;
    LL sum(LL x){return (x+1)*BIT1.sum(x)-BIT2.sum(x);}
    void add(int x,LL k){BIT1.update(x,k);BIT2.update(x,x*k);}
    LL getsum(int l,int r){return sum(r)-sum(l-1);}
    void update(int l,int r,LL x){add(l,x);add(r+1,-x);}
}BIT;

struct node{
    int l,r,sign,id;
};
vector<node> G[N];
void dfs(int x){
    id[in[x]=++cnt]=x;
    for(auto i:a[x]) dfs(i);
    out[x]=cnt;
}

int main()
{   
    int n,m;
    scc(n,m);
    l[1]=1;r[1]=n;
    for(int i=1;i<=m;i++){
        int x,y;    scc(x,y);
        a[x].pb(y); scc(l[y],r[y]);
    }
    dfs(1);
    int q; sc(q);
    for(int i=1;i<=q;i++){  
        int x,l,r;
        sccc(x,l,r);
        G[in[x]-1].pb(node{l,r,-1,i});
        G[out[x]].pb(node{l,r,1,i});
    }
    for(int i=1;i<=cnt;i++){
        int x=id[i];
        BIT.update(l[x],r[x],1);
        for(auto j:G[i]) 
            ans[j.id]+=1ll*j.sign*BIT.getsum(j.l,j.r);
    }
    for(int i=1;i<=q;i++) printf("%lld\n",ans[i]);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值