2021牛客多校#9 E-Eyjafjalla(离散化,线段树,离线)

题目链接

传送门

题目大意

火山国家有 n n n 个城市,编号为 1 1 1 n n n ,城市 1 1 1 是首都,有一座大火山。城市 i i i 的温度是 t i t_i ti
n n n 个城市用 n − 1 n-1 n1 条无向边相连,第 i i i 条边连接城市 u i u_i ui v i v_i vi 。如果 u i u_i ui v i v_i vi更接近首都,则 t u i > t v i t_{u_i}>t_{v_i} tui>tvi。首都的温度是最高的。
如果在城市 x x x爆发生存温度为 [ l , r ] [l,r] [l,r] 的病毒,求会感染多少城市。
若一个城市的温度位于 [ l , r ] [l,r] [l,r]之内,且它与另一个受感染的城市相连,则它也会被感染。

题解

由题目可得,整个国家是一个有 n − 1 n-1 n1条边的树。
且深度越小的节点温度越大,反之则更小。
我们用DFS序来储存这一棵树,则可以以较小的复杂度查找向上或向下的节点。
如果在城市 x x x 爆发病毒,那么会影响到它附近节点 废话 ,它有两种可能,一种是向上查找,一种是向下查找。
由于向上查找的可能遇到其他能向下找的点,所以我们优先向上查找,我们用倍增查询能符合温度条件的深度最小的节点,然后通过线段树查找符合节点。
由于查询过大,我们将它离线,用前缀和将它保存下来,因为节点可能为 1 0 9 10^9 109 ,所以我们通过离散化记录。

参考代码

#include<bits/stdc++.h>
#define pb push_back
#define ll long long
#define FOR(i,n,m) for(int i=n;i<=m;i++)
using namespace std;
const int N=1e5+5;
void read(int &x)
{
    int ret=0;
    char c=getchar();
    while(!isdigit(c))
        c=getchar();
    while(isdigit(c))
        ret=ret*10+c-'0',c=getchar();
    x=ret;
}
struct p{
    int l,r,sum;
}segtree[N*3<<2];
struct up{
    int id,num;
};
int dfn[N],edn[N],node[N],f[N][30],t[N],ans[N],b[N];
int cnt,n,m,q;
map<int,int>mp;
set<int> s;
vector<int> v[N];
vector<up> l[N],r[N];
void pushup(int x)
{
    segtree[x].sum=segtree[x<<1].sum+segtree[x<<1|1].sum;
}
void build(int x,int l,int r)
{
    segtree[x].l=l;
    segtree[x].r=r;
    segtree[x].sum=0;
    if(l==r)
        return;
    int mid=l+r>>1;
    build(x<<1,l,mid);
    build(x<<1|1,mid+1,r);
}
int query(int x,int l,int r)
{
    if(segtree[x].l==l && segtree[x].r==r)
        return segtree[x].sum;
    int mid=segtree[x].l+segtree[x].r>>1;
    if(r<=mid)
        return query(x<<1,l,r);
    if(l>mid)
        return query(x<<1|1,l,r);
    return query(x<<1,l,mid)+query(x<<1|1,mid+1,r);
}
void update(int x,int q)
{
    if(segtree[x].l==q && segtree[x].r==q)
    {
        segtree[x].sum++;
        return;
    }
    int mid=segtree[x].l+segtree[x].r>>1;
    if(q<=mid)
        update(x<<1,q);
    else
        update(x<<1|1,q);
    pushup(x);
}
void dfs(int x,int fa) //DFS序
{
    dfn[x]=++cnt;
    node[cnt]=x;
    f[x][0]=fa;
    FOR(i,1,20)
        f[x][i]=f[f[x][i-1]][i-1];
    FOR(i,0,v[x].size()-1)
    {
        if(v[x][i]==fa)
            continue;
        dfs(v[x][i],x);
    }
    edn[x]=cnt;
}
int main()
{
    read(n);
    FOR(i,1,n-1)
    {
        int x,y;
        read(x);
        read(y);
        v[x].pb(y);
        v[y].pb(x);
    }
    FOR(i,1,n)
    {
        read(t[i]);
        s.insert(t[i]);
    }
    dfs(1,0);
    read(m);
    FOR(i,1,m)
    {
        int x,xl,xr;
        read(x);
        read(xl);
        read(xr);
        s.insert(xl);
        if(t[x]<xl || t[x]>xr)
            continue;
        for(int j=20;j>=0;j--)
            if(t[f[x][j]]<=xr  && f[x][j])    //求符合条件里面深度最小的
                x=f[x][j];
        l[dfn[x]-1].push_back(up{i,xl});      //前缀
        r[edn[x]].push_back(up{i,xl});
    }
    int t1=1;
    set<int>::iterator it;
    for(it=s.begin();it!=s.end();it++)     //离散化
        mp[*it]=t1++;
    q=s.size();
    build(1,1,q);
    FOR(i,1,n)            //计算
    {
        update(1,mp[t[node[i]]]);
        if(l[i].size())
            FOR(j,0,l[i].size()-1)
                ans[l[i][j].id]=query(1,mp[l[i][j].num],q);
        if(r[i].size())
            FOR(j,0,r[i].size()-1)
                ans[r[i][j].id]=query(1,mp[r[i][j].num],q)-ans[r[i][j].id];
    }
    FOR(i,1,m)
        printf("%d\n",ans[i]);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值