1 条题解
-
0
自动搬运
来自洛谷,原作者为

推翻暴政
ด้้้้้็้้้็็็็็้้้้้็็็็็้้้้้้็็็็็้้้้้็็็็็้้้้้้็็็็็้้้้้็็็็็้้้้้้็็็็็้้้้้็็็็็้้้้推翻@joyskaka 的暴政搬运于
2025-08-24 22:27:10,当前版本为作者最后更新于2023-10-24 08:39:18,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
一道不算水的黄题。
首先,对于每个点放置的水果我们可以做出以下的处理:
因为每个点放置的水果 仅会影响 它的所有祖先节点,所以我们可以考虑记录 节点的祖先数量大小 (即它与它的所有祖先),注意要包含它自己。
现在的问题是,每个点会做出贡献多少次?显然是 次,我的推导过程如下:
注意到样例一解释:
用 表示选中状态:
- 贡献
- 贡献
- 贡献
- 贡献
- 贡献
- 贡献
- 贡献
- 贡献
我们考虑同样的想法, 中,二进制表示下每一位 会出现 次,就可以得到这个结论了。
故答案公式如下:
线性求出 ,使用 dfs 求出祖先数量 大小最后统计答案即可,时间复杂度 。
代码如下:
#include<bits/stdc++.h> using namespace std; //模数和2的逆元 需要用到2的逆元是因为我的2^(n-1)是用(2^n)/2求得的 非常不聪明 const long long mod=998244353,inv=499122177; const int MAXN=1000005; int n,a[MAXN],head[MAXN],tot; long long siz[MAXN],base,ans; struct edge{ int v,nex; } e[MAXN<<1]; //链式前向星建边 inline void add_edge(int u,int v){ e[++tot].v=v; e[tot].nex=head[u]; head[u]=tot; } //深搜求出祖先数量大小 inline void dfs(int x,int fa){ siz[x]=siz[fa]+1; for(int i=head[x];i;i=e[i].nex){ int y=e[i].v; if(y==fa) continue; dfs(y,x); } } //处理2^(n-1) inline void pre(){ base=1; for(int i=1;i<=n;i++) base=(base*2)%mod; base=(base*inv)%mod; } int main(){ scanf("%d",&n); pre(); for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<n;i++){ int x,y; scanf("%d%d",&x,&y); add_edge(x,y); add_edge(y,x); } dfs(1,1); //统计答案 注意三个数一起相乘可能会爆long long 所以我们拆成两两相乘 for(int i=1;i<=n;i++){ siz[i]=(siz[i]*a[i])%mod; ans+=base*siz[i]%mod; ans%=mod; } printf("%lld",ans); return 0; }感谢你的阅读,点个赞再走吧~
- 1
信息
- ID
- 6313
- 时间
- 1000ms
- 内存
- 128MiB
- 难度
- 3
- 标签
- (无)
- 递交数
- 0
- 已通过
- 0
- 上传者