牛客2021暑期训练9-E-Eyjafjalla
题意
给定一个以 1 1 1 为根的有根树,孩子的点权小于父亲的点权。多次询问,每次询问包含 x x x 节点的权值范围为 [ l , r ] [l, r] [l,r] 的极大连通块的大小
题解
病毒传播可以看作两个阶段,第一个阶段先上升到可感染的最高的一个节点 r t rt rt,第二阶段感染 r t rt rt 的子树中所有温度大于 l l l 的城市
第一阶段可以通过倍增法求得 r t rt rt
第二阶段相当于在 r t rt rt 的子树中查询权值大于 l l l 的节点个数,根据每个节点的 d f s dfs dfs 序建立线段树,存下区间最大最小值,然后在线段树上查询小于 l l l 的节点个数, r t rt rt 子树的节点数减去即可
代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+9;
int n,q,cnt,tot;
int T[N],L[N],R[N],f[N],fa[N],st[N][29],siz[N];
int nxt[N*2],ver[N*2],head[N];
struct node
{
int l,r,mxt,mit,sz;
}t[N*4];
void Add(int u,int v)
{
nxt[++cnt]=head[u];
head[u]=cnt;
ver[cnt]=v;
}
void Getdfn(int u,int fat)
{
L[u]=++tot; //L、R为dfs序
f[tot]=u; //f是dfs序与原节点对应关系
fa[u]=fat; //fa用来做倍增
siz[u]=1; //siz为子树大小
for(int i=head[u];i;i=nxt[i])
{
int v=ver[i];
if(v==fat) continue;
Getdfn(v,u);
siz[u]+=siz[v];
}
R[u]=tot;
}
void ST() //倍增
{
for(int i=1;i<=n;i++)
st[i][0]=fa[i];
for(int j=1;j<=20;j++)
for(int i=1;i<=n;i++)
st[i][j]=st[st[i][j-1]][j-1];
}
void Make_Tree(int u,int l,int r) //建树
{
t[u].l=l; t[u].r=r;
if(l==r)
{
t[u].sz=1;
t[u].mxt=T[f[l]]; //记录区间最大最小,f为对应节点
t[u].mit=T[f[l]];
return;
}
int mid=(l+r)>>1;
Make_Tree(u<<1,l,mid);
Make_Tree((u<<1)|1,mid+1,r);
t[u].sz=t[u<<1].sz+t[(u<<1)|1].sz;
t[u].mxt=max(t[u<<1].mxt,t[(u<<1)|1].mxt);
t[u].mit=min(t[u<<1].mit,t[(u<<1)|1].mit);
}
int Find_Max(int u,int maxn) //向上倍增找rt
{
for(int i=20;i>=0;i--)
if(T[st[u][i]]<=maxn)
u=st[u][i];
return u;
}
int Query(int u,int l,int r,int al,int ar,int e) //查询温度小于l的个数
{
if(al<=l&&r<=ar) //在区间里满足下面可直接返回,不满足一直向下找
{
if(t[u].mxt<e) return (r-l+1);
if(t[u].mit>=e) return 0;
}
int mid=(l+r)>>1;
if(ar<=mid) return Query(u<<1,l,mid,al,ar,e);
else if(al>mid) return Query((u<<1)|1,mid+1,r,al,ar,e);
else return Query(u<<1,l,mid,al,ar,e)+Query((u<<1)|1,mid+1,r,al,ar,e);
}
int main()
{
scanf("%d",&n);
for(int i=1;i<n;i++)
{
int u,v;
scanf("%d%d",&u,&v);
Add(u,v);
Add(v,u);
}
for(int i=1;i<=n;i++)
scanf("%d",&T[i]);
T[0]=1<<30;
Getdfn(1,0);
ST();
Make_Tree(1,1,n);
scanf("%d",&q);
while(q--)
{
int x,l,r;
scanf("%d%d%d",&x,&l,&r);
if(T[x]<l||T[x]>r)
{
printf("0\n");
continue;
}
int rt=Find_Max(x,r);
int tot=Query(1,1,n,L[rt],R[rt],l);
printf("%d\n",siz[rt]-tot);
}
return 0;
}