牛客多校第九场 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));
    }
}
View Code

 

posted @ 2021-08-15 15:19  我是菜狗QAQ  阅读(23)  评论(0编辑  收藏  举报