计蒜客CSP-J2模拟赛:函数

2023年10月6日 | 分类: 【编程】

【题面】

题目描述

对于两个非负整数 a,b ,如果 ab=b ,则称 ab 的子集,记作 ab 。其中 表示按“位或”运算。

给定一个长度为 2p 的序列 a0,a1,,a2p1
我们定义函数 f(i)=jiaj

f(11)=a0+a1+a2+a3+a8+a9+a10+a11
其中 0|11=111|11=112|11=113|11=118|11=119|11=1110|11=1111|11=11

请求出 i=02p1f(i)=f(0)+f(1)+f(2)++f(2p1) 的值。答案对 998244353 取模。

输入格式

输入的第一行包含一个正整数 p

第二行以空格隔开输入 2p 个整数,分别表示 a0,a1,,a2p1 ​。

输出格式

输出一行一个整数,表示答案的值。

数据范围

对于 100% 的数据, 1p18,0ai109

各测试点的附加限制如下表所示:

测试点编号 	p≤ 	特殊性质
1∼8 		5 	
9∼14 		9 	
15∼16 		13 	保证所有 a[i]​ 相等
17∼20 		18

格式说明

输出时每行末尾的多余空格,不影响答案正确性

输入、输出要求

要求使用「文件输入、输出」的方式解题,输入文件为 func.in,输出文件为 func.out

按:func 是 function 的缩写,意为函数。

样例输入1

2
0 1 2 3

样例输出1

9

样例解释1

f(0)=a0=0
f(1)=a0+a1=1
f(2)=a0+a2=2
f(3)=a0+a1+a2+a3=6

因此,答案是 0+1+2+6=9

样例输入2

4
6 15 7 15 19 4 1 5 15 20 5 14 11 20 3 4

样例输出2

856

样例解释2

f(0)=a0=6
f(1)=a0+a1=21
f(2)=a0+a2=13
f(3)=a0+a1+a2+a3=43
f(4)=a0+a4=25
f(5)=a0+a1+a4+a5=44
f(6)=a0+a2+a4+a6=33
f(7)=a0+a1+a2+a3+a4+a5+a6+a7=72
f(8)=a0+a8=21
f(9)=a0+a1+a8+a9=56
f(10)=a0+a2+a8+a10=33
f(11)=a0+a1+a2+a3+a8+a9+a10+a11=97
f(12)=a0+a4+a8+a12=51
f(13)=a0+a1+a4+a5+a8+a9+a12+a13=110
f(14)=a0+a2+a4+a6+a8+a10+a12+a14=67
f(15)=a0+a1+a2+a3+a4+a5+a6+a7+a8+a9+a10+a11+a12+a13+a14+a15=164

因此,答案是 6+21+13+43+25+44+33+72+21+56+33+97+51+110+67+164=856

【解析】

对于每个 ai 考虑贡献,会被计算 2npopcount(i) 次,其中 popcount(i) 表示 i 的二进制数中 1 的个数。

证明:一个完整二进制长度为 n ,那如果要让 ai 这个元素产生贡献,那么这个数在 i 所有为 1 的数位上必须都为 1 ,剩余部分可为 01 ,所以在 2n 中,会有 2npopcount(i) 包含 ai

时间复杂度 O(nlogn) ,如果使用 __builtin_popcount 可以做到 O(n)

【参考代码】

按:popcount 是 population count 的缩写,也叫 sideways sum (数位叠加和),是计算一个整数的二进制表示有多少位是 1 。

按:ios::sync_with_stdio(false); 参考 https://xinxixue.com/i2s7yn

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#include <bits/stdc++.h>
using namespace std;
const int N = 3e5 + 5, Mod = 998244353;
int main() {
    freopen("func.in", "r", stdin);
    freopen("func.out", "w", stdout);
    ios::sync_with_stdio(false);
    cin.tie(0);
    int p, n;
    cin >> p;
    n = (1 << p);
    long long sum = 0;
    for (int i = 0; i < n; i++) {
        int x;
        cin >> x;
        sum = (sum + (1ll << (p - __builtin_popcount(i))) * x % Mod) % Mod;
    }
    cout << sum;
    return 0;
}