2020-2021 ACM-ICPC Brazil Subregional Programming Contest E题 Party Company【树上倍增+树状数组维护】

题目链接

https://codeforces.ml/gym/102861/problem/E

题意

给你n个结点的树,每个点有一个权值,保证所有父节点的权值大于等于其子结点的权值,再给你m个范围为[l,r]的party和它们所在的点(the owner of this party)。对于某个点u,若其直接相连的父结点或子结点有party 且 u点权值在其父结点或子结点的party的[l,r]范围内,那么点u就加入该party。问:每个点加入的party个数。

思路

将左端点和右端点的限制分开来考虑。

首先我们知道初始时哪些点有party,那么对于每个party,可以先将其向上扩展(使用树上倍增),扩展到的最高点的权值小于等于party右端点,然后可以直接把这个party的拥有者变成扩展到的最高点。 这样处理之后,就已经满足了右端点的所有限制。

对于每个party向上扩展到的最高点ancester,将party的左端点放入数组保存,然后对树进行dfs,对于点u,将其拥有的所有party加入到树状数组中,然后查询点u的所有从祖先们继承来的party中的左端点<=val[u]的个数(这些都满足了左端点的限制),即为答案。回溯时注意清除从祖先们继承来的party即可。具体详见代码。

AC代码

#include 
using namespace std;
const int N=1e5,M=20;
int n,m,val[N+10],f[N+10][M+10],mx[N+10][M+10],tr[N+10],ans[N+10];
vector<int>g[N+10]; //  存图
vector<int>party[N+10]; // 存每个点初始时加入的所有party的左端点
void dfs1(int u,int fa) // 树上倍增,预处理得到f数组和mx数组
{
    f[u][0]=fa;
    mx[u][0]=val[fa];
    for(int i=1;i<=M;i++)
    {
        f[u][i]=f[f[u][i-1]][i-1];
        mx[u][i]=max(mx[u][i-1],mx[f[u][i-1]][i-1]); // max{ u向上走2^(i-1)这段的最大值, u向上走2^(i-1)到u再向上走2^(i-1)这段的最大值 }
    }
    for(auto v:g[u])
        if(v!=fa)dfs1(v,u);
}
int get_anc(int u,int w) // get ancestor
{
    for(int i=M;i>=0;i--)
        if(mx[u][i]<=w)u=f[u][i];
    return u;
}
void update(int i,int k) // 单点修改i+=k
{
    while(i<=N) // 不能写成i<=n!
    {
        tr[i]+=k;
        i+=(i&(-i));
    }
}
int query(int i) // 区间查询[1,i]
{
    int sum=0;
    while(i)
    {
        sum+=tr[i];
        i-=(i&(-i));
    }
    return sum;
}
void dfs2(int u,int fa)
{
    for(auto l:party[u]) // 当前点u的所有party加入到树状数组中维护
        update(l,1);
    ans[u]=query(val[u]); // 当前点u的所有从祖先们继承来的party(存于树状数组)中的左端点<=val[u]的个数
    for(auto v:g[u])
        if(v!=fa)dfs2(v,u);
    for(auto l:party[u]) // 回溯
        update(l,-1);
}
int main()
{
    ios::sync_with_stdio(false);
    int u,fa,l,r;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>val[i]>>fa;
        if(i!=fa)g[i].push_back(fa),g[fa].push_back(i);
    }
    dfs1(1,1); // 第一个参数是根结点,第二个参数是令根结点的父亲为它自己
    for(int i=1;i<=m;i++)
    {
        cin>>u>>l>>r;
        int anc=get_anc(u,r); // 点u能向上扩展的最高位置,即祖先结点ancestor
        party[anc].push_back(l);
    }
    dfs2(1,1);
    for(int i=1;i<=n;i++)
        i==n?printf("%d\n",ans[i]):printf("%d ",ans[i]);
    return 0;
}

参考

  1. 参考题解

  2. 官方题解https://codeforces.ml/gym/102861/attachments/download/12362/editorial-en.pdf
    2020-2021 ACM-ICPC Brazil Subregional Programming Contest E题 Party Company【树上倍增+树状数组维护】_第1张图片

你可能感兴趣的:(#,ACM-区域赛/网络赛,#,ACM-数据结构,算法,数据结构,倍增,树状数组,dfs)