牛客多校第九场 E Eyjafjalla (倍增+主席树
题意,有一个树,根节点为1,每个节点都有一个温度,保证离根节点越近温度越高。
现在有q次询问,每次在城市k爆发一种生存温度在[l,r]的病毒,问病毒会蔓延几个城市。
思路:赛场上其实是出了的,但是这道题出了点锅,而且感觉是一道还可以的数据结构题,所以记录一下。
首先对于一棵树,我们肯定不好直接从任意一个点找齐所有的点,但是如果是从根节点往下找,那就会好找很多,因为针对某棵子树我们维护各种都系都会方便很多。
那么对于每一次询问,我们不妨先找到离根节点最近的刚好小于等于r的城市。这一步可以用倍增找,当然树链剖分之类的算法应该也行,但是倍增写起来非常方便。
当我们找到了这个r,那么问题就转换成了对于一棵子树,我们找他下面点权大于等于l的点数,这个东西我们可以对dfs序建主席树来维护。
赛场上的锅出在,为了方便搞出一段区间,我直接把dfs序翻倍搞成了括号化序列,但是忘记把数组空间也翻倍了,导致wa了好久,然后他不报RE又没往那想,debug了好久才出
下附代码:
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+5; const int max0=20; int stampk; int fa[maxn][max0+1]; int Next[maxn*2],to[maxn*2],head[maxn],tot=0; int dep[maxn]; int dfnl[maxn],dfnr[maxn]; int t[maxn]; int sum[(maxn*2)<<5]; int ls[(maxn*2)<<5],rs[(maxn*2)<<5]; int cnt[maxn*2],b[maxn*2]; int rt[(maxn*2)<<5]; int tt=0; void build(int l,int r,int root) { sum[root]=0; if(l==r) return ; int mid=l+r>>1; build(l,mid,ls[root]=++tt); build(mid+1,r,rs[root]=++tt); } void update(int l,int r,int root,int last,int pos) { ls[root]=ls[last]; rs[root]=rs[last]; sum[root]=sum[last]+1; if(l==r) return ; int mid=l+r>>1; if(mid>=pos) update(l,mid,ls[root]=++tt,ls[last],pos); else update(mid+1,r,rs[root]=++tt,rs[last],pos); } int query(int l,int r,int root,int last,int pos) { if(l==r) return sum[root]-sum[last]; int mid=l+r>>1; int ans; if(pos<=mid) ans=query(l,mid,ls[root],ls[last],pos)+sum[rs[root]]-sum[rs[last]]; else ans=query(mid+1,r,rs[root],rs[last],pos); return ans; } void add(int a,int b){ Next[tot]=head[a],to[tot]=b; head[a]=tot++; } void dfs1(int x) { for(int i=1;i<=max0;i++) if(fa[x][i-1]) //在dfs(x)之前,x的父辈们的fa数组都已经计算完毕,所以可以用来计算x fa[x][i]=fa[fa[x][i-1]][i-1]; else break; //如果x已经没有第2^(i-1)个父亲了,那么也不会有更远的父亲,直接break for(int i=head[x]; i!=-1; i=Next[i]){ int y=to[i]; if(y!=fa[x][0]) //如果i不是x的父亲就是x的儿子 { fa[y][0]=x; //记录儿子的第一个父亲是x dep[y]=dep[y]+1; //处理深度 dfs1(y); } } } void dfs(int x,int pre){ dfnl[x]=++stampk; cnt[stampk]=x; for (int i=head[x]; i!=-1; i=Next[i]){ int y=to[i]; if (y!=pre) dfs(y,x); } dfnr[x]=++stampk; } int find(int x,int up){ int ad=max0; while (fa[x][ad]==0 && ad>=0) ad--; while (ad>=0){ while (fa[x][ad]==0 && ad>=0) ad--; while (t[fa[x][ad]]>up && ad>=0) ad--; if (ad<0) break; x=fa[x][ad]; } return x; } int main(){ int n; scanf("%d",&n); memset(fa,0,sizeof(fa)); tt=0; build(1,2*n,rt[0]=++tt); for (int i=1; i<=n; i++){ head[i]=-1; } for (int i=1; i<n; i++){ int x,y; scanf("%d%d",&x,&y); add(x,y); add(y,x); } dfs1(1); stampk=0; dfs(1,-1); for (int i=1; i<=n; i++) { scanf("%d",&t[i]); b[i]=t[i]; } b[n+1]=0; sort(b+1,b+2+n); int all=unique(b+1,b+2+n)-b-1; for (int i=1; i<=2*n; i++){ int tmp=lower_bound(b+1,b+1+all,t[cnt[i]])-b; if (tmp==1) tmp=0; update(1,2*n,rt[i]=++tt,rt[i-1],tmp); } int q; scanf("%d",&q); while (q--){ int p,l,r; scanf("%d%d%d",&p,&l,&r); if (t[p]<l || t[p]>r){ printf("0\n"); continue; } int zz=find(p,r); // printf("%d\n",zz); // printf("%d %d %d\n",dfnr[zz],dfnl[zz]-1,lower_bound(b,b+1+all,l)-b+1); printf("%d\n",query(1,2*n,rt[dfnr[zz]],rt[dfnl[zz]-1],lower_bound(b+1,b+1+all,l)-b)); } }