P7159 「dWoi R1」Sweet Fruit Chocolate 题解
一道不算水的黄题。
首先,对于每个点放置的水果我们可以做出以下的处理:
因为每个点放置的水果 仅会影响 它的所有祖先节点,所以我们可以考虑记录
现在的问题是,每个点会做出贡献多少次?显然是
注意到样例一解释:
用
S 表示选中状态:
S=000$ 贡献 $0 S=001$ 贡献 $1 S=010$ 贡献 $2 S=011$ 贡献 $3 S=100$ 贡献 $6 S=101$ 贡献 $7 S=110$ 贡献 $8 S=111$ 贡献 $9
我们考虑同样的想法,
故答案公式如下:
线性求出
代码如下:
#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;
}
感谢你的阅读,点个赞再走吧~