“蔚来杯“2022牛客暑期多校训练营(加赛) Here is an Easy Problem of Zero-chan

原题题面:https://ac.nowcoder.com/acm/contest/38727/H

题目大意:
给定包含n个节点的有根树,根节点为1,定义
f ( x ) = ∏ i = 1 n l c a ( x , i ) f(x)=\prod^n_{i=1}lca(x,i) f(x)=i=1nlca(x,i)
有q次询问,每次询问给定x,求 f ( x ) f(x) f(x)后缀0数量
n , q n,q n,q均不超过 1 e 5 1e5 1e5

分析:
遇事不决先画图
在这里插入图片描述
由上图,可以发现,对于点 A A A,所有点与它的 l c a lca lca皆在 A A A r o o t root root的路径中
所以可以考虑树形dp
因为 10 = 2 ∗ 5 10=2*5 10=25,我们可以预处理每个节点 i i i的2和5数量 ,记为 t w o [ i ] , f i v e [ i ] two[i],five[i] two[i],five[i]
定义 d p [ v ] . t w o dp[v].two dp[v].two为在点v时 l c a lca lca积中2的数量
d p [ v ] . f i v e dp[v].five dp[v].five为在点v时 l c a lca lca积中5的数量
看图可知, d p [ s o n i ] dp[son_i] dp[soni]的状态是由它上一级的祖先状态的得到的
所以对于每一个非叶子节点 u u u的所有 s o n i son_i soni
状态转移方程为:
d p [ s o n i ] . t w o = d p [ u ] . t w o − s i z e [ v ] ∗ ( t w o [ u ] − t w o [ s o n i ] ) dp[son_i].two=dp[u].two-size[v]*(two[u]-two[son_i]) dp[soni].two=dp[u].twosize[v](two[u]two[soni])
d p [ s o n i ] . f i v e = d p [ u ] . f i v e − s i z e [ v ] ∗ ( f i v e [ u ] − f i v e [ s o n i ] ) dp[son_i].five=dp[u].five-size[v]*(five[u]-five[son_i]) dp[soni].five=dp[u].fivesize[v](five[u]five[soni])
对于每次询问的x, a n s x = m i n ( d p [ x ] . t w o , d p [ x ] . f i v e ) ans_x=min(dp[x].two,dp[x].five) ansx=min(dp[x].two,dp[x].five)
时间复杂度: O ( n l o g n + n + q ) O(n logn+n+q ) O(nlogn+n+q),是绰绰有余的
代码:

#include<bits/stdc++.h>
#define LL long long

const int MXN=1e5+7;

using namespace std; 
int two[MXN],five[MXN];
int n;

struct Node{
	int Two,Five;
};
int siz[MXN];
Node dp[MXN];

vector<int> g[MXN];
void DFS_1(int now,int fa){
	int v; siz[now]=1;
	for(int i=0;i<g[now].size();i++){
		v=g[now][i];
		if(v==fa) continue;
		DFS_1(v,now);
		siz[now]+=siz[v];
	}
}

void DFS_2(int now,int fa){
	int v;
	for(int i=0;i<g[now].size();i++){
		v=g[now][i];
		if(v==fa) continue;
		dp[v].Two=dp[now].Two-siz[v]*two[now]+siz[v]*two[v];
		dp[v].Five=dp[now].Five-siz[v]*five[now]+siz[v]*five[v];
		DFS_2(v,now);
	}
}

int main(){
	int q,x,u,v;
	scanf("%d%d",&n,&q);
	for(int i=1;i<n;i++){
		scanf("%d%d",&u,&v);
		g[u].push_back(v);
		g[v].push_back(u);
	}
	for(int i=1;i<=n;i++){
		int t=i;
		while(t%2==0){
			two[i]++;
			t>>=1;
		}
		while(t%5==0){
			five[i]++;
			t/=5;
		}
	}
	DFS_1(1,-1);
	dp[1].Two=siz[1]*two[1];
	dp[1].Five=siz[1]*five[1];
	DFS_2(1,-1);
	while(q--){
		scanf("%d",&x);
		printf("%d\n",min(dp[x].Two,dp[x].Five));
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值