2021牛客暑期多校训练营9 E.Eyjafjalla(树状数组/主席树)

这篇博客详细介绍了如何利用树状数组和主席树解决区间查询问题,特别是在处理每个节点子树中特定条件的点数量时。首先,通过深度优先搜索获取每个节点的dfs序,并构建数据结构。然后,对于每个查询,确定查询区间内满足条件的点,使用树状数组或主席树进行高效查询。最后,给出完整的C++代码实现,包括在线和离线两种解决方案。
摘要由CSDN通过智能技术生成

题目链接:https://ac.nowcoder.com/acm/contest/11260/E

分析

先跑一边dfs处理出每个点的dfs序就能得到以每个点为根节点的子树所拥有的点的dfs序的区间,如图
在这里插入图片描述
由于是dfs序,每个点的子树所包含的所有点的dfs序是连续的。
我们先根据每个查询的 l l l r r r,向上找到距离开始点最远的在区间内的点,答案就是以该点为根的子树大小减去该点的dfs序区间内温度小于等于 x − 1 x-1 x1的点的数量。
由于查询次数较多,我们可以记录每次要查询的dfs序区间以及 x − 1 x-1 x1,用树状数组离线查找。

代码

树状数组做法
#include <bits/stdc++.h>
using namespace std;
typedef long long LL;

const int maxn = 100000 + 10;
int n, q, c[maxn], ans[maxn];
int deep[maxn],pre[maxn][64];
vector<int> g[maxn];
int cnt = -1, rr[maxn], tp[maxn], s[maxn], dfn[maxn];

struct x {
    int d;
    int p;
    bool operator <(x c)const {
        return  d < c.d;
    };
} a[maxn];
struct query {
    int l, r, x;
    int id;
    bool operator <(query c) const {
        return x < c.x;
    }
} Q[maxn];
void add(int x) {
    while(x <= n) {
        c[x]++;
        x += (x & (-x));
    }
}
int sum(int x) {
    int  s = 0;
    while(x > 0) {
        s += c[x];
        x -= (x & (-x));
    }
    return s;
}


void dfs(int u, int fa)
{
	deep[u]=deep[fa]+1;
	pre[u][0]=fa;
	for(int i=1;(1<<i)<=deep[u];i++)
		pre[u][i]=pre[pre[u][i-1]][i-1];
	
	s[u] = 1;
	a[u].p = ++cnt;
	dfn[u] = cnt;
	for(int i=0;i<g[u].size();i++)
	{
		if(g[u][i] == fa) continue;
		dfs(g[u][i], u);
		s[u] += s[g[u][i]];
	}
	rr[u] = cnt;
}

int main() {
    scanf("%d", &n);
    for(int i=1;i<n;i++)
    {
    	int u,v;
    	scanf("%d%d",&u,&v);
    	g[u].push_back(v);
    	g[v].push_back(u);
	}
	for(int i=1;i<=n;i++) scanf("%d",&tp[i]), a[i].d = tp[i];
	dfs(1, 0);
    sort(a + 1, a + 1 + n);
    scanf("%d",&q);
    for(int i = 0; i < q; i++) {
    	Q[i].id = i;
    	int u,L,R;
        scanf("%d%d%d", &u, &L, &R);
        if(tp[u] < L || tp[u] > R)
        {
        	Q[i].x = -1;
        	ans[i] = 0;
        	continue;
		}
		int l=0,r=deep[u]-deep[1];
		int v = u;
        while(l<r)
        {
            int du=u;
            int mid=(l+r+1)/2;
            for(int i=30;i>=0;i--) if((mid>>i)&1) du=pre[du][i];
            if(tp[du]>R) r=mid-1;
            else
			{
				l=mid;
				v = du;
			}
        }
        Q[i].l = dfn[v];
        Q[i].r = rr[v];
        ans[i] = s[v];
        Q[i].x = L - 1;
    }
    sort(Q, Q + q);
    int l = 1;
    for(int i = 0; i < q; i++) {
    	if(Q[i].x == -1) continue;
        x tmp;
        tmp.p = 1;
        tmp.d = Q[i].x;
        int pos = upper_bound(a + 1, a + 1 + n, tmp) - a;
        while(l < pos) {
            add(a[l].p);
            l++;
        }
        ans[Q[i].id] -= sum(Q[i].r) - sum(Q[i].l - 1);
    }
    for(int i = 0; i < q; i++)
        printf("%d\n", ans[i]);
    return 0;
}
主席树做法
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn = 100000 + 10;
int n, q, m;
int deep[maxn],pre[maxn][64];
vector<int> g[maxn];
int cnt, rr[maxn], tp[maxn], dfn[maxn];
int t[maxn * 20], sum[maxn * 20], L[maxn * 20], R[maxn * 20], idx, a[maxn], b[maxn];

void build(int &rt, int l, int r)
{
	rt = ++idx;
	sum[rt] = 0;
	if(l == r) return;
	int mid = (l + r) >> 1;
	build(L[rt], l, mid);
	build(R[rt], mid + 1, r);
}

void update(int &rt, int pre, int l, int r, int pos)
{
	rt = ++idx;
	L[rt] = L[pre], R[rt] = R[pre], sum[rt] = sum[pre] + 1;
	if(l == r) return;
	int mid = (l + r) >> 1;
	if(pos <= mid) update(L[rt], L[pre], l, mid, pos);
	if(pos > mid) update(R[rt], R[pre], mid + 1, r, pos);
}

int query(int tl, int tr, int l, int r, int pos)
{
	if(l == r) return sum[tr] - sum[tl];
	int mid = (l + r) >> 1;
	int res = 0;
	if(pos <= mid)
	{
		res += sum[R[tr]] - sum[R[tl]];
		res += query(L[tl], L[tr], l, mid, pos);
	}
	else res += query(R[tl], R[tr], mid + 1, r, pos);
	return res;
}

void dfs(int u, int fa)
{
	deep[u]=deep[fa]+1;
	pre[u][0]=fa;
	for(int i=1;(1<<i)<=deep[u];i++)
		pre[u][i]=pre[pre[u][i-1]][i-1];
	
	dfn[u] = ++cnt;
	a[cnt] = tp[u];
	for(int i=0;i<g[u].size();i++)
	{
		if(g[u][i] == fa) continue;
		dfs(g[u][i], u);
	}
	rr[u] = cnt;
}

int main() {
    scanf("%d", &n);
    for(int i=1;i<n;i++)
    {
    	int u,v;
    	scanf("%d%d",&u,&v);
    	g[u].push_back(v);
    	g[v].push_back(u);
	}
	for(int i=1;i<=n;i++) scanf("%d",&tp[i]), b[i] = tp[i];
	dfs(1, 0);
	sort(b + 1, b + 1 + n);
	m = unique(b + 1, b + 1 + n) - b - 1;
	build(t[0], 1, m);
	for(int i=1;i<=cnt;i++)
	{
		int pos = lower_bound(b + 1, b + 1 + m, a[i]) - b;
		update(t[i], t[i - 1], 1, m, pos);
	}
    scanf("%d",&q);
    for(int i = 0; i < q; i++) {
    	int u,ql,qr;
        scanf("%d%d%d", &u, &ql, &qr);
        if(tp[u] < ql || tp[u] > qr)
        {
        	printf("0\n");
        	continue;
		}
		int l=0,r=deep[u]-deep[1];
		int v = u;
        while(l<r)
        {
            int du=u;
            int mid=(l+r+1)/2;
            for(int i=30;i>=0;i--) if((mid>>i)&1) du=pre[du][i];
            if(tp[du]>qr) r=mid-1;
            else
			{
				l=mid;
				v = du;
			}
        }
        int tt = lower_bound(b + 1, b + 1 + m, ql) - b;
        printf("%d\n",query(t[dfn[v] - 1], t[rr[v]], 1, m, tt));
    }
    
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值