牛客多校第九场 Eyjafjalla 树上问题 倍增 dfs序上建点权可持久化线段树

这一场是我们学校出的,结果出的非常残暴,第二题就比较复杂。第一题是个签到,我去写了,让队友去看了第二题。第一题签了半个多小时终于一次a了,然后去问队友第二题。听懂题意以后,我说了句:诶,我感觉这个题是不是可以用可持久化。队友:???
如果熟悉可持久化数据结构,这题其实就是一个sb题,不过我们三个都没怎么接触过可持久化,所以一次AC还是挺激动的。

题意

给你一棵树,每个点都有一个点权,而且父亲的点权大于他任意儿子的点权。让你从一个点开始走,要求只能走到点权在 l,r 范围内的点,问你能走到一共多少个点。有 1e5 次询问。

做法

首先可以利用倍增找到这个点可以走到的最高的祖先,这个问题就变成了在这个祖先的子树种查询有多少个点的值大于 l
这样先用dfs序把子树问题变成子段问题,然后离散化后向主席树按照dfs序的顺序插入每个点的值,然后差分容斥,用 第 r 次插入减去第 l-1 次插入的结果,得出答案。
其实可以用树状数组离线做,但是当时第一反应就是主席树,所以直接上去写了。
上代码:

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <algorithm>
#include <cstring>
#include <map>
#define int long long
#define MAXN 5000005
using namespace std;
typedef long long ll;
const ll maxn=300010;
struct node
{
    int ls,rs,v;
}st[MAXN];int ptr;
void iniSt(int &p,int l,int r)
{
    p=++ptr;
    if(l==r) return;
    int mid=(l+r)/2;
    iniSt(st[p].ls,l,mid);
    iniSt(st[p].rs,mid+1,r);
}
int modify(int p,int l,int r,int x,int v)
{
    int np=++ptr;
    st[np]=st[p];
    if(l==r)
    {
        st[np].v+=v;
        return np;
    }
    int mid=(l+r)/2;
    if(x<=mid)
        st[np].ls=modify(st[np].ls,l,mid,x,v);
    else
        st[np].rs=modify(st[np].rs,mid+1,r,x,v);
    st[np].v=st[st[np].ls].v+st[st[np].rs].v;//用两个儿子更新爸爸
    return np;
}
int rt[MAXN];
int query(int p,int l,int r,int al,int ar)
{
    if(al<=l&&ar>=r)
        return st[p].v;
    if(ar<l||al>r)return 0;
    int mid=(l+r)/2;
    return query(st[p].ls,l,mid,al,ar)+query(st[p].rs,mid+1,r,al,ar);
}
struct Node
{
	ll v;
	ll next;
}e[2*maxn];
ll n,p[maxn],t=0;
ll tpr[maxn],New[maxn],ano[maxn];
ll ask[maxn][4];
map<ll,ll> mp;
void insert(ll u,ll v)
{
	e[t].v=v;
	e[t].next=p[u];
	p[u]=t++;
}
ll dfn[maxn],parent[maxn][30],son[maxn],times=0;
void dfs(ll u,ll pre)
{
	dfn[u]=++times;
	ano[times]=tpr[u];
	son[u]=1;
	for(ll i=p[u];i!=-1;i=e[i].next)
	{
		ll v=e[i].v;
		if(v!=pre)
		{
			dfs(v,u);
			son[u]+=son[v];
			parent[v][0]=u;
		}
	}
}
ll getroot(ll x,ll l,ll r)
{
	while(1)
	{
		ll now=0;
		while(tpr[parent[x][now]]<=r && parent[x][now]!=0)
			now++;
		now--;
		if(now==-1)
			return x;
		x=parent[x][now];
	}
}
signed main()
{
	memset(p,-1,sizeof(p));
	scanf("%lld",&n);
	for(ll i=1;i<=n-1;i++)
	{
		ll u,v;
		scanf("%lld%lld",&u,&v);
		insert(u,v);
		insert(v,u);
	}
	for(ll i=1;i<=n;i++)
	{
		scanf("%lld",&tpr[i]);
		New[i]=tpr[i];
	}
	ll cnt=n,q;
	scanf("%lld",&q);
	for(ll i=1;i<=q;i++)
	{
		scanf("%lld%lld%lld",&ask[i][0],&ask[i][1],&ask[i][2]);
		New[++cnt]=ask[i][1];
		New[++cnt]=ask[i][2];
	}
	sort(New+1,New+1+cnt);
	ll m=unique(New+1,New+1+cnt)-New-1;
	for(ll i=1;i<=m;i++)
		mp[New[i]]=i;
    for(ll i=1;i<=n;i++)
		tpr[i]=mp[tpr[i]];
	dfs(1,-1);

	iniSt(rt[0],1,m);
    for(int i=1;i<=times;i++)
    {
        rt[i]=modify(rt[i-1],1,m,ano[i],1);
	}
	for(ll level=1;level<=20;level++)
		for(ll i=1;i<=n;i++)
			parent[i][level]=parent[parent[i][level-1]][level-1];
	for(ll i=1;i<=q;i++)
	{
		ll x=ask[i][0],l=ask[i][1],r=ask[i][2];
		if(tpr[x]<mp[l] || tpr[x]>mp[r])
		{
			printf("0\n");
			continue;
		}
		ll root=getroot(x,mp[l],mp[r]);
		cout<<query(rt[dfn[root]+son[root]-1],1,m,mp[l],m)-query(rt[dfn[root]-1],1,m,mp[l],m)<<endl;
	}
	return 0;
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值