2020-2021 ACM-ICPC Brazil Subregional Programming Contest E题 Party Company树上倍增+树状数组维护
Posted nefu_ljw
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 <bits/stdc++.h>
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;
}
参考
以上是关于2020-2021 ACM-ICPC Brazil Subregional Programming Contest E题 Party Company树上倍增+树状数组维护的主要内容,如果未能解决你的问题,请参考以下文章
2019-2020 ACM-ICPC Brazil Subregional Programming Contest (7/10)
(寒假GYM开黑)2018-2019 ACM-ICPC Brazil Subregional Programming Contest
2018-2019 ACM-ICPC Brazil Subregional Programming Contest PART (10/13)
2018-2019 ACM-ICPC Brazil Subregional Programming Contest
gym101908 [2018-2019 ACM-ICPC Brazil Subregional Programming Contest] 题解
2018-2019 ACM-ICPC Brazil Subregional Programming Contest B. Marbles