专栏文章

题解:P13875 [蓝桥杯 2024 省研究生组] 植物生命力

P13875题解参与者 3已保存评论 2

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
2 条
当前快照
1 份
快照标识符
@mio173u3
此快照首次捕获于
2025/12/02 11:41
3 个月前
此快照最后确认于
2025/12/02 11:41
3 个月前
查看原文

P13875 [蓝桥杯 2024 省研究生组] 植物生命力 - 题解

思路:

看起来不太可做,但我们可以注意到题目中有这样一句话:对于所有的测试用例,1ain1 \le a_i \le na1,a2,,ana_1, a_2, \ldots, a_n 互不相同。这说明所有的节点权值组成了一个 11nn 的排列,这是一个良好的性质。
对于这道题,一个显然的思路就是:对于所有的 ii 查询子树内小于 aia_i 的权值个数,再减去子树内被 aia_i 整除的权值个数,前者需要使用可持久化线段树才能解决,而后者直接计算也十分困难,所以我们考虑另一个思路:计算每一个节点对答案产生的贡献,由此产生了一个显然的算法:当遍历到该节点时将这个节点权值插入线段树,查询线段树内插入的权值有多少个比该权值大,再减去线段树内是该权值整数倍的权值,然后从线段树中删除该节点。线段树的部分容易解决,而减去整数倍的部分却几乎不可做,似乎陷入了僵局。但此时回到开头提到的性质:节点权值是一个 11nn 的排列,那么我们就可以直接对每一个权值枚举它的整数倍是否在线段树中,对于权值 11,要枚举 n1\frac{n}{1} 次;对于权值 22,要枚举 n2\frac{n}{2} 次,以此类推,对于整棵树,总共要枚举:
n1+n2+n3++nn\frac{n}{1}+\frac{n}{2}+\frac{n}{3}+\cdots+\frac{n}{n}
这么多次,即 nlnnn\ln{n} 次,可以通过此题。
一些显然的优化:可以用树状数组代替线段树,以减小常数;把权值插入树状数组的同时打标记,这样在枚举整数倍的时候可以做到 O(1)O(1) 查询而无需在树状数组上查询。

代码:

CPP
vector<int> mp[100005];
int w[100005];
bool vis[100005];
int n,rt;
long long ans;
int c[100005];
void add(int x,int v)
{
	for(;x<=n;x+=lowbit(x))	c[x]+=v;
}
int qry(int x)
{
	int res=0;
	for(;x;x-=lowbit(x))	res+=c[x];
	return res;
}
void dfs(int u,int f)
{
	vis[w[u]]=1;
	add(w[u],1);
	for(int v:mp[u])
	{
		if(v==f)	continue;
		dfs(v,u);
	}
	for(int i=2;i*w[u]<=n;i++)
		ans-=vis[i*w[u]];
	ans+=qry(n)-qry(w[u]);
	vis[w[u]]=0;
	add(w[u],-1);
}
char QQ,coin;
decltype(QQ+coin) main()
{
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>rt;
	for(int i=1;i<=n;i++)
		cin>>w[i];
	for(int i=1;i<n;i++)
	{
		int u,v;
		cin>>u>>v;
		mp[u].push_back(v);
		mp[v].push_back(u);
	}
	dfs(rt,0);
	cout<<ans<<"\n";
	return ~(-1);
}

评论

2 条评论,欢迎与作者交流。

正在加载评论...