• 高维前缀和


    高维前缀和

    主要内容

    昨天(\(\texttt{2022.2.28}\))打 ARC 的 D 题时,恍然发现我不会高维前缀和,匆匆来学一下。

    比如二维前缀和 \(s_{i,j}\) 表示在一个二维平面上从 \((1,1)\)\((i,j)\) 的所有点的权值之和,我们定义高维前缀和 \(s_{p_1,p_2,p_3,\cdots,p_n}\) 表示所有 \((q_1,q_2,q_3,\cdots,q_n)\) 并且满足 \(q_1\le p_1,q_2\le p_2,\cdots,q_n\le p_n\) 的所有点的权值之和。

    如何求解高维前缀和呢?

    举个例子,二维前缀和可以用一下代码来求:

    for(int i=1;i<=n;i++)
    	 for(int j=1;j<=m;j++)
    	 	 sum[i][j]+=sum[i-1][j];
    for(int i=1;i<=n;i++)
    	 for(int j=1;j<=m;j++)
    	 	 sum[i][j]+=sum[i][j-1];
    

    类比一下,高维前缀和可以用如下方式求解:

    // 假设前缀和是 q 进制的。 
    int val[N]={1,q,q^2,q^3,...,q^{N-1}};
    for(int opt=1;opt<=N;opt++)
    	 for(int i=0;i<=MAX;i++)
    	 	 if((i/val[opt])%q>=1)
    	 	 	 sum[i]+=sum[i-val[opt]];
    

    每一位都从这一位减一转移过来,其实高维前缀和也可以被想为一个 Dp 的过程,类似 Dp 的转移更好理解。

    例题

    CF772D Varying Kibibits

    定义 \(f(S)\) 表示一个由若干个数组成的集合 \(S\) 中,返回值十进制每一位上的数为所有数中这一位的最小值。

    给定 \(n\) 个数组成的集合 \(T\),求:

    \[\oplus_{x=0}^{999999}\left(x\times\left(\left(\sum_{S\subseteq T,S\not=\emptyset,f(S)=x}\left(\sum_{y\in S}y\right)^2\right)\bmod 1000000007\right)\right) \]

    \(n\le 10^6,t_i< 10^6\)

    想到高维前缀和之后,难点就是怎样从当前这一位比它大 \(1\) 的位置转移过来。

    主要问题是平方和的合并问题,发现其实就是这样一个过程:

    \[a_1^2+a_2^2~\texttt{add}~b_1^2+b_2^2+b_3^2\Rightarrow(a_1+b_1)^2+(a_1+b_2)^2+(a_1+b_3)^2+(a_1+b_1)^2+(a_2+b_2)^2+(a_2+b_3)^2 \]

    这样好办了,在每一位记下所有集合的 \(sum\) 的和、所有集合的 \(sum^2\) 的和与集合数量,直接转移即可。

    发现这样求出来的是 \(f(x)\) 大于等于它的答案,所以最后还要进行一次差分。

    \(\texttt{code}\)

    [ARC100C] Or Plus Max

    直接维护最大和第二大的即可。

    CF1208F Bits And Pieces

    给定 \(n\) 个数的数组 \(d\),找到 \(i<j<k\)\(i,j,k\),使得 \(d_i|(d_j \& d_k)\) 最大。

    \(3\le n\le 10^6,0\le d_i\le 2\times 10^6\)

    考虑枚举每一位的 \(d_i\),预处理可行的 \(\infty\oplus d_i\) 的子集的最大值(如果 \(d_i\) 这一位为 \(1\),答案肯定有 \(1\))。

    但是好像假了,去看题解了。。

    \(\bigstar\texttt{Hint-1}\):我们可以考虑计算出 \(d_i\) 部分包含的最小的 \(i\)\(d_j\&d_k\) 部分包含的最大的 \(j\)

    上边的可以用高维前缀和快速搞定,之后好像有不会了。。

    \(\bigstar\texttt{Hint-2}\):之后发现答案是从高到第决定的,只用维护第 \(i\) 位为 \(1\) 的最大值即可,从高位到低位判断这一位是否为 \(1\),每次 \(\mathcal{O(n)}\) 验证,这样压缩到了 \(\mathcal{O(maxbit\times n)}\)。(验证部分代码如下)

    inline bool check(int x)
    {
    	 for(int i=0;i<=MAX;i++)
    	 	 if(dp[i].mini<dp[x^(x&i)].maxk) return true;
    	 return false;
    }
    

    这道题都要看两个 hint,我太菜了呀!!

    CF449D Jzzhu and Numbers

    给定长为 \(n\) 的序列 \(\{a\}\),计算从中选出一些数使得它们与起来等于 \(0\) 的方案数。

    \(n,a_i\le 10^6\)

    有一个牛逼的状态:\(dp(s)\) 表示选出的数与起来为 \(s\) 的超集的方案数

    发现其实不用一开始就将方案数算出来,可以记下可以选择的数的数量,那么方案数直接就是 \(2^{dp(s)}\)

    那么计算出超集后再容斥就是答案了。

    \(\bigstar\texttt{important}\):记得减去空集的情况,因为每次计算都存在空集。

    n=rd();
    for(int i=1,x;i<=n;i++) x=rd(),dp[x]++;
    for(int p=1;p<=20;p++)
    	 for(int i=0;i<=1000000;i++)
    	 	 if(!(i & (1<<(p-1))))
    	 	 	 dp[i]+=dp[i+(1<<(p-1))];
    for(int i=0;i<=1000000;i++)
    {
    	 if(__builtin_popcount(i)&1) ans=(ans-ksm(2,dp[i])+1+mod)%mod;
    	 else ans=(ans+ksm(2,dp[i])-1)%mod;
    }
    printf("%d\n",ans%mod);
    
  • 相关阅读:
    bzoj1934 Vote 善意的投票 最小割(最大匹配)
    poj3417 Network 树上差分+LCA
    bzoj1076 奖励关 期望dp
    bzoj1087 互不侵犯King 状压dp+bitset
    bzoj1041 圆上的整点 数学
    bzoj 1085骑士精神 迭代深搜
    CodeForces 1043D Mysterious Crime 区间合并
    2018.12.14 浪在ACM 集训队第九次测试赛
    2018.12.9 中国石油大学第四次新生训练赛题解
    2018.12.8 中国石油大学第三次新生训练赛题解
  • 原文地址:https://www.cnblogs.com/EricQian/p/15949664.html
Copyright © 2020-2023  润新知