P7159 「dWoi R1」Sweet Fruit Chocolate 题解

· · 题解

一道不算水的黄题。

首先,对于每个点放置的水果我们可以做出以下的处理:

因为每个点放置的水果 仅会影响 它的所有祖先节点,所以我们可以考虑记录 i 节点的祖先数量大小 Size_i(即它与它的所有祖先),注意要包含它自己。

现在的问题是,每个点会做出贡献多少次?显然是 2^{n-1} 次,我的推导过程如下:

注意到样例一解释:

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

我们考虑同样的想法,[0,2^n) 中,二进制表示下每一位 1 会出现 2^{n-1} 次,就可以得到这个结论了。

故答案公式如下:

Ans=2^{n-1}\times\large\sum\limits_{i=1}^n Size_i

线性求出 2^{n-1},使用 dfs 求出祖先数量 Size_i 大小最后统计答案即可,时间复杂度 O(n)

代码如下:

#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;
}

感谢你的阅读,点个赞再走吧~