子集的子集

10 篇文章 0 订阅

题目

题目描述
有一个大小为 n 的集合 S, 它由 n 个二元组 (i,xi)(1≤i≤n) 组成。 我们用序列 si(1≤i≤n)来表示属于 S 的二元组 (i,si)。 定义一个集合的权值为属于它的二元组的 x 之积, 即二元组的第二个元素之积。 求出 S 的所有非空子集的所有非空子集的权值之和。

输入
输入包含两行。第一行输入一个正整数 n, 第二行输入 n 个正整数,表示 s1∼n。

输出
输出包含一行一个整数,表示答案。 答案对 998244353=119×223+1 取模。

样例
输入
2
2 3
输出
16
解释
S={(1,2),(2,3)}, 为了方便,我们省略二元组的第一个元素,记 S={2,3}。

{2,3} 的非空子集有:{2,3},{2},{3}。

{2,3} 的非空子集的权值之和为:2×3+2+3=11。

{2} 的非空子集的权值之和为:2。

{3} 的非空子集的权值之和为:3。

所以答案为 16。

数据规模与约定
本题采用子任务测试的形式。 仅当你通过了子任务中所有的测试点, 你才能得到该子任务对应的分数,否则不得分。

对于 100% 的数据,有 1≤si<998244353。

子任务 1(12 分):n≤8。

子任务 2(20 分):n≤15。

子任务 3(27 分):n≤5×10^3,∀i,si=1。

子任务 4(41 分):n≤5×10^3。

子任务 5(100 分):n≤10^5。


题解

–听说是一道dp呢,不过首先要知道一个神奇的概念对答案的贡献
因为任何一个大小为 x 的子集的子集 都包含在 2^(n-x) 个子集里
所以说,这个子集的子集对答案的贡献就是 它的元素之积 * 2^(n-x)

–然后是状态定义:f[i][j]表示在前 i 数中选 j 个数出来构成子集的积的和 (必须选第i个)
转移方程:f[i][j] = f[i−1][j] + ai × f[i−1][j−1]
注意:f[i][0]=1
最终的答案是:f[n][1]*2^(n-1)+f[n][2]*2^(n-2)……f[n][n]*2^(n-n)
时间复杂度:O(n^2)

至于子任务5,我只有一脸冷漠
听大佬说:
考虑生成函数。将每个数看作 Ai(x) = 1 + ai*x,则 ∏(n,i=1)
Ai 的 i 次项系数就表示在 n 个数中选 i 个数它
们的积的和。由于模数为 998244353,因此使用分治 NTT,
在 O(n*log^2n) 的时间复杂度内求出f[n][i]。
就交给大佬们咯


代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=5005;
const int mod=998244353;

int n;
int a[MAXN];
long long f[MAXN][MAXN];
long long ans;

long long Pow(int b){
    long long ans=1,a=2;
    while(b){
        if(b&1)
            ans=ans*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return ans;
}

int main(){
    cin>>n;
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    for(int i=1;i<=n;i++){
        f[i-1][0]=1;
        for(int j=1;j<=i;j++){
            f[i][j]=f[i-1][j]+a[i]*f[i-1][j-1];
            f[i][j]%=mod;
        }
    }
    for(int i=1;i<=n;i++){
        ans+=f[n][i]*Pow(n-i);
        ans%=mod;
    }
    cout<<ans;
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,我理解了您的需求。下面是一个有子集子集下还有子集的前端代码示例,帮助您实现渲染树状列表的效果。 前端代码示例(使用微信小程序): ```html <!-- index.wxml --> <view class="tree"> <block wx:for="{{ treeData }}" wx:key="id" wx:for-item="item"> <view class="item {{ item.visible ? 'visible' : '' }}" bindtap="handleItemClick" data-id="{{ item.id }}"> {{ item.name }} </view> <block wx:if="{{ item.children && item.children.length > 0 }}"> <view class="children"> <block wx:for="{{ item.children }}" wx:key="id" wx:for-item="child"> <view class="item {{ child.visible ? 'visible' : '' }}" bindtap="handleItemClick" data-id="{{ child.id }}"> {{ child.name }} </view> <block wx:if="{{ child.children && child.children.length > 0 }}"> <view class="children"> <block wx:for="{{ child.children }}" wx:key="id" wx:for-item="grandchild"> <view class="item {{ grandchild.visible ? 'visible' : '' }}" bindtap="handleItemClick" data-id="{{ grandchild.id }}"> {{ grandchild.name }} </view> <!-- 可以继续嵌套更深层级的子集 --> </block> </view> </block> </block> </view> </block> </block> </view> ``` ```javascript // index.js Page({ data: { treeData: [ { id: 1, name: '节点1', visible: true, children: [ { id: 2, name: '子节点1', visible: true, children: [ { id: 4, name: '孙子节点1', visible: true, }, { id: 5, name: '孙子节点2', visible: true, }, ], }, { id: 3, name: '子节点2', visible: true, }, ], }, ], }, handleItemClick(event) { const clickedId = event.currentTarget.dataset.id; const { treeData } = this.data; // 递归遍历树状列表数据,隐藏其他项,显示被点击项 const traverseTree = (data) => { data.forEach(item => { if (item.id === clickedId) { item.visible = true; } else { item.visible = false; } if (item.children && item.children.length > 0) { traverseTree(item.children); } }); }; traverseTree(treeData); this.setData({ treeData: [...treeData], }); }, }); ``` 在这个示例中,我们使用了嵌套的`<block>`和`<view>`组件来渲染树状列表。每个列表项都有一个`visible`字段来控制显示状态,同时可以嵌套更深层级的子集。 点击列表项时,会触发`handleItemClick()`函数。函数会递归遍历树状列表数据,根据点击项的`id`来修改其他项的显示状态,从而实现隐藏其他项、显示被点击项的效果。 希望以上示例对您有所帮助。如果您有更多问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值