把整数转换成2的n次方的和数组

大概这样:125 => [1, 4, 8, 16, 32, 64]

写几种实现: Ruby:

2.3.1 :022 > 125.to_s(2).reverse.chars.map.with_index{|b, i| b.to_i.zero? ? nil : 2**i}.compact 
 => [1, 4, 8, 16, 32, 64] 

或者:

2.3.1 :028 > 125.to_s(2).reverse.chars.map.with_index{|b,i| b.to_i*2**i}.reject(&:zero?) 
 => [1, 4, 8, 16, 32, 64] 

Haskell: Haskell的整数转成二进制序列比较麻烦,先写一个函数来实现:

import Numeric (showHex, showIntAtBase)
import Data.Char (intToDigit)

bits :: Int -> [Int]
bits x = map (\n -> read [n]::Int) $ showIntAtBase 2 intToDigit x ""
main = do
    print $ bits 125

bits 函数用来将一个整数转换成 二进制的整数 数组 如 bits 125 => [1,1,1,1,1,0,1]

第二步,构造2的n次方序列[1,2,4,8,16...] 然后 将n次方序列和 反转后的二进制序列对应位相乘,最后过滤掉0。

main = do
    print . filter (/=0) . zipWith (*) [ 2^n | n <- [0..] ] . reverse $ bits 125

Haskell利用位移算法实现,依赖Data.Bits包里的 位与右移 函数:

import Data.Bits

bits2 :: Int -> [Int]
bits2 x | x <=0         = []
        | otherwise     = bits2 (shiftR x 1) ++ [x .&. 1]
--  bits2 函数即通过位移实现二进制List,由于需要reverse,所以下面实际用到的bits稍微修改。
--  bits2 返回的是二进制序列的倒序,避免里reverse一次。
        
bitsList :: Int -> [Int]
bitsList x = filter (/=0) . zipWith (*) [2^n | n<-[0..] ] $ bits x
            where 
                bits n  | n <=0 = []
                        | otherwise = n .&. 1 : bits (shiftR n 1)

main = do
    print $ bitsList 125    
=> [1,4,8,16,32,64]

这样的实现方法似乎更加Haskell,

Js实现:

> (125).toString(2).split('').reverse().map((e,i) => parseInt(e) * Math.pow(2,i)).filter( e => e>0 )
[ 1, 4, 8, 16, 32, 64 ]

Js位移方法实现,每次右移1位,同时判断末尾是否位1,为1则,加入。

function bits(n) {
  var result = [];
  var m = 0;
  while (n > 0) {
    if (n & 1) {
      result.push(Math.pow(2, m));
    }
    m++;
    n >>= 1;
  }
  return result;
}

console.log(bits(125));
> [ 1, 4, 8, 16, 32, 64 ]

或者写成for,简单一些

function bits(n) {
  var result = [];
  for (var m = 0; n > 0; m++, n >>= 1) {
    if (n & 1) {
      result.push(Math.pow(2, m)); // es7: 2**m
    }
  }
  return result;
}

es7支持 指数运算符 **,es6还不支持,chrome浏览器已经支持,更加简化。

Julia

# 复合函数,(Julia >=0.6版本,已经内置该函数,so easy!)
∘(f::Function, g::Function) = x -> f(g(x))

function binaryLists(n::Int)
  b = [x for x in lstrip(bits(n), '0')]
  filter(x->x>0, map(*, [2^y for y=0:length(b)-1], map(parse ∘ string, reverse(b))))
end

print(binaryLists(125))

=> [1,4,8,16,32,64]

julia在这个问题上处理有些复杂,无法轻松构造出一个无限列表,map针对多个List参数时候,必须保证所有List长度一致,这点区别于Haskell的 zipWith,zipWith两个列表按最短的为准,忽略多余的。为了简化代码定义了一个复合函数(),这样就不用写 x-> parse(string(x)) julia支持两个List直接用 list1 .* list2 来相乘对应位,可以替换 map(*, list1, list2)

function binaryLists(n::Int)
  b = [x for x in lstrip(bits(n), '0')]
  filter(x->x>0, [2^y for y=0:length(b)-1] .* map(parse ∘ string, reverse(b)))
end

适当化简一些。

转载于:https://my.oschina.net/jsk/blog/742036

You are given positive integers N,K, and an integer sequence of length N: A=(A 1 ​ ,A 2 ​ ,…,A N ​ ). Find 1≤l≤r≤N ∑ ​ ( l≤i≤r ∑ ​ A i ​ ) K , modulo 998244353. Constraints 1≤N≤2×10 5 1≤K≤10 0≤A i ​ <998244353 All input values are integers. Input The input is given from Standard Input in the following format: N K A 1 ​ A 2 ​ … A N ​ Output Print the answer. Sample Input 1 Copy 3 2 3 1 2 Sample Output 1 Copy 75 The value is A 1 2 ​ +A 2 2 ​ +A 3 2 ​ +(A 1 ​ +A 2 ​ ) 2 +(A 2 ​ +A 3 ​ ) 2 +(A 1 ​ +A 2 ​ +A 3 ​ ) 2 =3 2 +1 2 +2 2 +4 2 +3 2 +6 2 =75. Sample Input 2 Copy 1 10 0 Sample Output 2 Copy 0 Sample Input 3 Copy 10 5 91 59 85 60 57 72 12 3 27 16 Sample Output 3 Copy 428633385 Be sure to find the sum modulo 998244353. C++AC代码,中文!!!过样例,时间复杂度O(NK)。提示: 代码解释 前缀预处理:计算数组的前缀,使得每个元素表示从开头到当前位置的。 幂次预处理:计算每个前缀的各个幂次,以便后续快速计算。 后缀预处理:计算每个幂次的后缀,用于快速累加子数组的贡献。 组合数预处理:计算组合数C(K, t),用于多项式展开中的系数。 多项式展开计算:利用多项式定理展开每个子数组的K次方,并通过预处理的后缀数组快速累加结果,最终得到答案。 通过这种法,我们能够将时间复杂度优化到O(NK),适用于较大的输入规模。
03-30
<think>好的,我现在得仔细看看这个问题。题目大意是说,给定一个长度为N的整数序列A,我们需要计算所有可能的子数组的K次方的总,然后对998244353取模。子数组是指连续的一段元素,也就是1≤l≤r≤N的所有区间,计算每个区间内元素的K次方,然后把这些全加起来。这个结果可能很大,所以必须取模。 首先,我得考虑如何高效地计算这个总。因为N可以达到2e5,K最多是10,显然不能暴力枚举所有子数组,然后逐个计算K次方,这样时间复杂度是O(N&sup2;K),对于N=2e5来说绝对不行。必须找数学上的优化法,或者动态规划之类的技巧。 看提示里的思路,提到了前缀预处理、幂次预处理、后缀预处理、组合数预处理,以及多项式展开。这让我想到可能需要用到多项式展开中的二项式定理或者更一般的展开式,把每个子数组的K次方拆分成各个项的乘积之和,然后通过预处理这些项的贡献来加速计算。 比如,假设子数组S=sum_{i=l}^r A_i。那么S^K可以展开为各种组合的乘积。例如,当K=2时,S&sup2;等于各元素的平加上两倍每两个不同元素的乘积。这时候,每个元素的贡献可能可以分解成不同维度的统计。 但如何将这些展开后的项与所有可能的子数组联系起来呢?可能需要将每个项的贡献独立计算,然后累加起来。例如,对于展开后的项中的A_i * A_j,我们需要统计所有包含ij的子数组的数量,然后将这个数量乘以A_i*A_j,再乘以对应的组合系数。 但这里的问题可能更复杂,因为每个子数组中的各个元素可能在不同的位置,而我们需要考虑它们的各种组合。比如,当K=3时,可能需要处理三个不同位置的元素的乘积。 或者,另一个思路是,利用前缀数组来快速计算各个子数组。假设我们有一个前缀数组P,其中P[i]表示前i个元素的。那么子数组[l, r]的就是P[r] - P[l-1]。然后,我们需要计算所有可能的lr对应的(P[r] - P[l-1])^K的总。 这时候,我们可以展开这个表达式,利用二项式定理或者多项式展开。例如,对于每个项中的组合系数,我们需要计算C(K, t) * (-1)^{K-t} * P[r]^t * P[l-1]^{K-t}。然后将所有这样的项相加。 但这样的话,我们需要预处理每个前缀的各个幂次,然后对于每个r,计算对应的各个t的组合,然后累加前面的l-1的前缀的K-t次方的总。这可能涉及到前缀的后缀数组,这样可以快速查询某个位置之后的所有前缀的幂次之和。 比如,假设我们有一个数组sum_t,其中sum_t[i]表示从i到n的所有P[i]^t的值之和。那么对于当前的r,当计算t次方时,我们可能需要sum_{l=1}^r P[l-1]^{K-t},而如果我们预处理了这些后缀,就可以O(1)或O(K)的时间查询。 具体来说,假设我们已经预计算了每个位置i,各个幂次m的P[i]^m的值,然后对于每个m,维护一个后缀数组,其中suffix[m][i]表示从i到n的所有P[j]^m的。这样,当处理到某个r时,我们需要找到所有l<=r的情况,此时l-1的范围是0到r-1。因此,对于每个t,组合项中的部分是sum_{x=0}^{r-1} P[x]^{K-t},而利用后缀数组的话,这可能可以表示为suffix[K-t][0] - suffix[K-t][r]。或者可能我的预处理式需要调整,比如每个后缀数组保存的是从i开始到末尾的,或者类似的结构。 这可能涉及到对每个幂次m,预先计算一个前缀数组,或者后缀数组。比如,当处理到r时,我们需要知道前面所有x(0到r-1)的P[x]^m之和。这可以通过前缀数组pre_sum[m][r]来表示,pre_sum[m][r] = sum_{x=0}^{r-1} P[x]^m。这样,当处理到r时,对于某个t,需要pre_sum[K-t][r],即前r个元素(0到r-1)的P[x]^{K-t}的。 然后,对于每个r,我们可以展开(P[r] - P[x])^K,其中x从0到r-1。根据二项式展开式: (P[r] - P[x])^K = sum_{t=0}^K C(K, t) * (-1)^{K-t} * P[r]^t * P[x]^{K-t} 将所有的x的贡献加起来,就是对于每个r,总共有sum_{x=0}^{r-1} (P[r] - P[x})^K。而整个问题的答案就是所有r的该值的总。 因此,总答案等于sum_{r=1}^n sum_{x=0}^{r-1} sum_{t=0}^K C(K,t) * (-1)^{K-t} * P[r]^t * P[x]^{K-t} 交换求顺序,可以得到: sum_{t=0}^K C(K,t) * (-1)^{K-t} * sum_{r=1}^n P[r]^t * sum_{x=0}^{r-1} P[x]^{K-t} 然后,这个式子的计算可以拆解为预处理每个t的组合系数,然后对于每个r,将P[r]^t乘以对应的前缀中的P[x]^{K-t}的前r项的,然后乘以对应的系数,累加到结果中。 为了高效计算,我们需要预处理每个可能的幂次m(0到K)的前缀。比如,对于每个m,我们维护一个数组pre_sum[m],其中pre_sum[m][i]表示前i个元素(x=0到i-1)的P[x]^m的。这样,当处理到r时,sum_{x=0}^{r-1} P[x]^{m} 就等于 pre_sum[m][r]。 因此,整个算法的步骤大概是: 1. 预处理组合数C(K, t),其中t从0到K。这可以通过递推或者直接计算。 2. 计算前缀数组P,其中P[0]=0,P[i]=P[i-1]+A[i-1],注意这里可能需要调整数组索引,因为原题中的数组是A_1到A_N,而代码中可能从0开始。 3. 预处理每个位置的P[i]^m的值,其中m从0到K。这可以通过遍历每个i,计算各个幂次的值,并保存下来。 4. 预处理每个幂次m的前缀数组pre_sum[m]。pre_sum[m][i] = sum_{x=0}^{i-1} P[x]^m。这样,当处理到r时,sum_{x=0}^{r-1} P[x]^m就是pre_sum[m][r]。 5. 对于每个r(从1到N),遍历t从0到K。计算对应的项:C(K, t) * (-1)^{K-t} * P[r]^t * pre_sum[K-t][r]。然后将所有这些项相加,并累加到最终结果中。 这样,时间复杂度是O(N*K),因为每个r需要处理K+1个t的情况,而每个步骤的处理都是O(1)。预处理组合数的时间是O(K&sup2;),预处理幂次前缀的时间也是O(N*K),所以总的时间复杂度是可行的。 现在,需要考虑具体的实现细节: 首先,组合数的计算。因为K最大是10,所以可以用动态规划或者直接计算。例如,C(K, t)可以用递推的式计算,或者直接使用组合数的公式。注意要模998244353。 然后,幂次的计算。对于每个i,我们需要计算P[i]的各个幂次,从0到K次。比如,当K=10时,每个i要计算P[i]^0=1,P[i]^1=P[i],直到P[i]^10。这可以通过逐次相乘实现。例如,初始时pows[i][0] = 1,然后pows[i][m] = pows[i][m-1] * P[i] % MOD。这可能比单独用快速幂计算每个幂次要高效。 接下来,前缀数组的预处理。对于每个m,pre_sum[m][i] = pre_sum[m][i-1] + P[i-1]^m。这里需要注意i的范围,例如,pre_sum[m][0] = 0,pre_sum[m][1] = P[0]^m,pre_sum[m][2] = P[0]^m + P[1]^m,依此类推。或者,可能数组的索引需要仔细设计。 假设前缀数组的pre_sum[m]的长度是N+1,其中pre_sum[m][r]表示前r个元素(即x从0到r-1)的P[x]^m之和。这样,当处理到r时,sum_{x=0}^{r-1} P[x]^m等于pre_sum[m][r]。 在实现时,对于每个m,我们遍历i从0到N-1,并将pre_sum[m][i+1] = pre_sum[m][i] + pow_P[i][m],其中pow_P是保存每个P[i]的各个幂次的数组。 现在,具体步骤: 步骤1:计算组合数C(K, t)。这里因为K不大,可以用递推的式。例如: C = 二维数组,大小为K+1 × K+1。初始化C[0][0] =1。对于n从1到K: C[n][0] =1,C[n][n]=1。对于k从1到n-1: C[n][k] = (C[n-1][k-1] + C[n-1][k}) % MOD. 但是当K=10时,这可能足够高效。或者,可以用公式直接计算每个C(K, t),因为K很小。 步骤2:计算前缀数组P。例如,P[0] =0,P[i] = (P[i-1] + A[i-1]) % MOD。注意MOD是998244353,且每个A[i]的值可能已经取模过,所以需要每次加法都取模。 步骤3:预处理每个P[i]的各个幂次,从0到K。例如,对于每个i,预先计算pow_P[i][m] = P[i]^m mod MOD。这可能通过循环实现: for每个i: pow_P[i][0] =1 for m从1到K: pow_P[i][m] = pow_P[i][m-1] * P[i] % MOD 这样,每个i需要O(K)时间,总共有O(NK)时间。 步骤4:预处理pre_sum数组。对于每个m(0<=m<=K),pre_sum[m][0] =0。然后,对于每个i从0到N-1: pre_sum[m][i+1] = (pre_sum[m][i] + pow_P[i][m]) % MOD. 这样,每个m的处理时间是O(N),总共有O(NK)时间。 步骤5:遍历每个r(从1到N),然后对于每个t从0到K,计算组合数C(K, t)乘以 (-1)^(K-t),乘以pow_P[r-1][t](因为P[r]在代码中对应的数组索引可能是r-1?或者需要仔细看数组的定义),乘以 pre_sum[K-t][r]。然后将所有这些项累加到结果中。 这里需要注意,r对应的P[r]在数组中的索引。例如,原题中的前缀数组可能定义为P[0]=0,P[1]=A1,P[2]=A1+A2,等等。而代码中的数组可能存储为P[0], P[1], ..., P[N],其中P[i]是前i个元素的。因此,当处理子数组[l..r]时,其为P[r] - P[l-1]。所以,在代码中,每个r(从1到N)对应的是原题中的r。那在代码中,假设我们的P数组的长度是N+1,其中P[0] =0,P[1] =A[0], P[2] =A[0]+A[1],等等。这样,处理的时候,当r从1到N时,对应的P[r]是正确的。 因此,在步骤3中的pow_P数组的i的范围应该是0到N,其中i对应的是P[i]。例如,pow_P[i][m]是P[i]的m次幂。那么,当处理到r(从1到N)时,对应的P[r]的幂次是pow_P[r][t]。而pre_sum[K-t][r]则表示前r个元素(x从0到r-1)的P[x]^{K-t}的总。因为pre_sum[m][r]等于sum_{x=0}^{r-1} pow_P[x][m}。例如,当r=1时,sum是P[0]^m。 所以,对于每个r,遍历t从0到K,计算项: C(K, t) * (-1)^(K-t) * pow_P[r][t] * pre_sum[K-t][r] 然后,所有这些项的总即为当前r的贡献,将所有这些贡献累加起来,就是最终答案。 需要注意符号的处理,例如,(-1)^(K-t)可能为负数,所以在模运算中需要转化为正数。例如,当K-t为奇数时,系数是-1;否则是+1。在模运算中,-1等价于MOD-1。所以,可以将系数计算为 ( (K-t) %2 ==0 ? 1 : -1 ),然后取模。 或者,可以提前计算符号数组。例如,对于每个t,符号是 (-1)^(K-t) = (-1)^{K} * (-1)^{-t} = (-1)^K * (-1)^t。或者直接,因为K-t的奇偶性决定了符号。例如,当K-t为偶数时,符号是1;否则是-1。例如,当K-t是奇数时,符号是-1,此时系数为C(K, t) * (-1) ,否则系数为C(K,t)*1。 现在,将这些步骤综合起来,编写代码的大致框架: 预处理组合数C(K, t): C = [[0]*(K+1) for _ in range(K+1)] C[0][0] =1 for n in 1..K: C[n][0] =1 C[n][n] =1 for k in 1..n-1: C[n][k] = (C[n-1][k-1] + C[n-1][k}) % MOD 但实际上,我们只需要计算C[K][t],因为t的范围是0到K。所以可以简化: 计算组合数C(K, t)的值,t从0到K。例如,可以用递推的式,或者直接计算。例如,C(K, t) = K!/(t! (K-t)! ), 但需要模运算下的逆元。但是当K最大是10的时候,这可以用循环直接计算。 或者,另一种法: 组合数C(K, t)可以按如下式计算: C = [0]*(K+1) C[0] =1 for i in range(1, K+1): C[i] = C[i-1] * (K -i +1) // i 但这里需要注意模运算下的除法,即乘以逆元。例如,在模MOD下,除法需要用逆元。但MOD是质数,所以可以这样做。 不过,当K=10时,手动计算组合数可能更简单,比如: C(K, t)的值可以用一个数组预先计算。比如,当K=2时,C(2,0)=1,C(2,1)=2,C(2,2)=1。 所以在代码中,可能需要预处理组合数C[K][t],其中t从0到K。 在C++中,可能直接用一个二维数组存储组合数。但这里可能用递推的法更高效。 现在,回到问题。假设我们已经正确计算了组合数C(K, t),符号项为sign = (-1)^(K-t)。在模运算中,我们可以将sign表示为pow(-1, K-t),然后模MOD。例如,当K-t为奇数时,sign为MOD-1,否则为1。 例如,在代码中,可以预先为每个t计算sign = 1 if (K-t) %2 ==0 else -1,然后将这个值模MOD转换为正数。 所以,每个项的系数是:comb = C(K,t) * sign。然后,这个comb需要模MOD。 现在,编写代码的大致步骤: 读取输入N, K,然后读取数组A,长度为N。 计算前缀数组P,其中P[0]=0,P[i] = (P[i-1] + A[i-1]) % MOD,i从1到N。 预处理每个P[i]的各个幂次,从0到K。例如,pow_P是一个二维数组,pow_P[i][m] = P[i]^m mod MOD。 预处理前缀数组pre_sum,其中pre_sum[m][r] = sum_{x=0}^{r-1} pow_P[x][m} mod MOD。对于每个m,pre_sum[m]是一个长度为N+1的数组。 然后,初始化ans=0。对于每个r从1到N: sum_r = 0 for t in 0..K: m = K -t comb = C(K, t) sign = (-1)^(K-t) term = comb * sign % MOD term = term * pow_P[r][t] % MOD pre = pre_sum[m][r] term = term * pre % MOD sum_r = (sum_r + term) % MOD ans = (ans + sum_r) % MOD 这样,最终ans就是结果。 需要注意的点: 1. pow_P数组的预处理是否正确。例如,对于每个i(0到N),计算其各个幂次。比如,pow_P[i][0] =1,然后对于每个m从1到K,pow_P[i][m] = pow_P[i][m-1] * P[i] % MOD. 2. pre_sum数组的预处理是否正确。pre_sum[m][r]表示前r个元素的前缀。例如,对于m=2,pre_sum[2][r] = sum_{x=0}^{r-1} P[x]^2. 3. 符号的计算是否正确。例如,当K-t是偶数时,符号是+1,否则是-1。在模运算中,-1等价于MOD-1。 例如,sign = 1 if (K-t) %2 ==0 else -1。在代码中,可以写为 sign = 1,如果K-t是偶数;否则,sign = MOD-1。 或者,sign = pow(-1, K-t),在模MOD下。比如,当K-t是偶数时,pow(-1, K-t) =1;奇数时为-1。这在模运算中可以写成: int sign = 1; if( (K-t) %2 ) sign = MOD-1; 但需要注意,在代码中,K-t可能为负数?比如,当t超过K时?但根据t的范围是0到K,所以 K-t 的取值是0到K,所以不会出现负数。例如,当t=K时,K-t=0,符号是1。 所以,对于每个t,sign是 (-1)^(K-t) = (-1)^(K-t)。这可以计算为: sign = 1 if (K-t) %2 ==0 else MOD-1. 例如,在代码中: int s = (K - t) % 2; int sign = (s ==0 ? 1 : MOD-1); 这样,就可以正确计算符号。 现在,将这些步骤翻译成C++代码: 首先,预处理组合数。这里,因为K最多是10,可以预先计算组合数数组。例如: vector<long long> C(K+1); C[0] = 1; for (int t = 1; t <= K; ++t) { // 计算C(K, t) // 这里可以用递推式:C(K,t) = C(K, t-1) * (K - t +1)/t // 但需要注意模下的除法,所以需要用逆元 // 或者,直接计算阶乘,然后用逆元 // 但因为K小,可以这样计算: C[t] = 1; for (int i=1; i<=t; ++i) { C[t] = C[t] * (K -i +1) / i; } } 但这种法在模运算下有问题,因为除法无法直接处理。所以更安全的式是,使用递推法计算组合数,或者预计算阶乘逆元。 例如,可以使用递推式C(n, k) = C(n-1, k-1) + C(n-1, k}。当n=K时,可以计算C[K][t]。 所以,可以创建一个二维数组comb,其中comb[k][t]表示C(k, t)。例如: vector<vector<long long>> comb(K+1, vector<long long>(K+1, 0)); comb[0][0] = 1; for (int n=1; n<=K; ++n) { comb[n][0] = 1; comb[n][n] = 1; for (int t=1; t<n; ++t) { comb[n][t] = (comb[n-1][t-1] + comb[n-1][t]) % MOD; } } 然后,当处理到K时,C(K, t)就是comb[K][t]。 这样预处理组合数是正确的。 接下来,预处理前缀数组P。例如: vector<long long> P(N+1, 0); for (int i=1; i<=N; ++i) { P[i] = (P[i-1] + A[i-1]) % MOD; } 预处理pow_P数组。例如,pow_P是二维数组,每个元素是P的幂次: vector<vector<long long>> pow_P(N+1, vector<long long>(K+1)); for (int i=0; i<=N; ++i) { pow_P[i][0] = 1; for (int m=1; m<=K; ++m) { pow_P[i][m] = pow_P[i][m-1] * P[i] % MOD; } } 预处理pre_sum数组。对于每个m,pre_sum[m]是数组,长度N+1: vector<vector<long long>> pre_sum(K+1, vector<long long>(N+1, 0)); for (int m=0; m<=K; ++m) { pre_sum[m][0] = 0; for (int r=1; r<=N; ++r) { pre_sum[m][r] = (pre_sum[m][r-1] + pow_P[r-1][m]) % MOD; } } 这里需要注意,pre_sum[m][r]表示前r-1项的P[x]^m的,因为x的范围是0到r-1-1?或者可能我在这里的索引搞错了。 例如,当r=1时,pre_sum[m][1] = sum_{x=0}^0 P[x]^m = P[0]^m. 而r=1对应的x是0到0。 那么,在pre_sum[m]的构造中,pre_sum[m][r]等于前r项的x是0到r-1的? 或者,pre_sum[m][r] = sum_{x=0}^{r-1} P[x]^m. 那在构造pre_sum的时候,初始化pre_sum[m][0] =0。对于每个r从1到N+1: pre_sum[m][r] = pre_sum[m][r-1] + pow_P[r-1][m} 因为当r=1时,x=0到0的项是pow_P[0][m}, 也就是P[0]^m. 这样,pre_sum[m][r] = sum_{x=0}^{r-1} P[x}^m. 是的,所以构造pre_sum的循环是正确的。 然后,处理每个r从1到N: long long ans =0; for (int r=1; r<=N; ++r) { long long sum_r =0; for (int t=0; t<=K; ++t) { int m = K - t; long long c = comb[K][t]; int s = (K - t) %2; long long sign = (s ==0) ? 1 : MOD-1; long long term = c * sign % MOD; term = term * pow_P[r][t] % MOD; term = term * pre_sum[m][r] % MOD; sum_r = (sum_r + term) % MOD; } ans = (ans + sum_r) % MOD; } 这样,最终的ans就是正确的结果吗? 验证一下样例输入1: Sample Input 1: 3 2 3 1 2 那么,A数组是[3,1,2]。前缀P是: P[0]=0 P[1]=3 P[2]=4 P[3]=6 pow_P[i][m]: 比如,i=0: pow_P[0][0]=1 pow_P[0][1]=0 pow_P[0][2]=0 i=1: pow_P[1][0]=1 pow_P[1][1]=3 pow_P[1][2]=9 i=2: pow_P[2][0]=1 pow_P[2][1]=4 pow_P[2][2]=16 i=3: pow_P[3][0]=1 pow_P[3][1]=6 pow_P[3][2]=36 pre_sum[m][r]: 对于m=0,pre_sum[0][r] = sum_{x=0}^{r-1} 1, 所以是r项的,即r. pre_sum[0][1] =1 pre_sum[0][2] =2 pre_sum[0][3] =3 pre_sum[0][4]=4 ? 不,原题中的N是3,所以r的范围是1到3. 对于m=0: pre_sum[0][1] =1 (x=0的pow_P[0][0}=1) pre_sum[0][2] =1+1=2 (x=01) pre_sum[0][3] =2+1=3 (x=0,1,2) pre_sum[0][4] =3+1=4 (x=0,1,2,3)?但原题中r的范围是1到3,所以预处理pre_sum的r从0到3. 可能这里需要仔细看pre_sum的构造。 pre_sum[m][r] 是sum_{x=0}^{r-1} pow_P[x][m}。例如,当r=3时,sum是x=0,1,2。 所以对于m=2: pre_sum[2][r=1] =0的平是0. pre_sum[2][r=2] =0^2 +3^2=9. pre_sum[2][r=3] =0+9+4^2=9+16=25. pre_sum[2][r=4]=25+6^2=25+36=61. 但原题中的r处理范围是1到3(因为N=3),所以当r=3时,pre_sum[m=2][3] =25. 现在,处理每个r: 当K=2,每个项t的取值是0,1,2. 例如,r=1: 对于t=0: m= K -t =2-0=2. sign = (-1)^(2-0)=1. comb= C(2,0)=1. term =1 *1 * pow_P[1][0} * pre_sum[2][1}. pow_P[1][0}=1,pre_sum[2][1}=0^2=0. term=1*1*1 *0=0. t=1: m=2-1=1. sign= (-1)^(1) =-1 → MOD-1. comb= C(2,1)=2. term=2*(MOD-1) * pow_P[1][1} * pre_sum[1][1}. pow_P[1][1}=3. pre_sum[1][1}=0^1=0. term=2*(MOD-1)*3*0=0. t=2: m=2-2=0. sign= (-1)^0=1. comb= C(2,2)=1. term=1*1 * pow_P[1][2} * pre_sum[0][1}. pow_P[1][2}=9. pre_sum[0][1}=1(因为pre_sum[0][1}是sum_{x=0}^0 P[x]^0=1). 所以 term=1*1 *9 *1=9. sum_r=0+0+9=9. ans=9. 处理r=2: t从0到2: t=0: m=2-0=2. sign=1. comb=1. pow_P[2][0}=1. pre_sum[2][2} =0^2 +3^2=9. term=1*1 *1*9=9. t=1: m=1. comb=2. sign=MOD-1. pow_P[2][1}=4. pre_sum[1][2}=0+3=3. term=2*(MOD-1) *4 *3= 2*3*4*(MOD-1). 因为MOD是998244353,所以需要模运算。但这里计算的话,例如: term = 2 * (MOD-1) % MOD → 2*(-1) mod MOD → -2 → MOD-2. 所以 term = (2 * (MOD-1)) % MOD → 998244351. 然后,乘以4 → 998244351 *4 mod MOD → ( -2 *4 ) mod MOD → -8 mod MOD → 998244345. 然后乘以3 → 998244345 *3 mod MOD → (-8 *3) mod MOD → -24 → MOD-24=998244329. 所以,term是998244329. t=2: m=0. comb=1. sign=1. pow_P[2][2}=16. pre_sum[0][2}=2. term=1*1 *16 *2=32. sum_r=9 + (998244329) +32. 但需要模运算: sum_r= (9 + 998244329) mod MOD → 9-24= -15 mod MOD → MOD-15=998244338. 加上32,得到 (998244338 +32) mod MOD → (998244370) mod MOD → 998244370 - 998244353 =17. 所以 sum_r=17. ans=9 +17=26. 处理r=3: t从0到2. t=0: m=2. sign=1. comb=1. pow_P[3][0}=1. pre_sum[2][3}=0^2 +3^2 +4^2=0+9+16=25. term=1*1*1*25=25. t=1: m=1. comb=2. sign=MOD-1. pow_P[3][1}=6. pre_sum[1][3}=0+3+4=7. term=2*(MOD-1)*6*7 → 2* (-1)*6*7 = -84 → mod MOD → 998244353-84=998244269. t=2: m=0. comb=1. sign=1. pow_P[3][2}=36. pre_sum[0][3}=3. term=1*1 *36*3=108. sum_r=25+998244269+108. 计算模: 25 +998244269 = 998244294 → mod MOD → 998244294-998244353= -59 → MOD-59=998244294. 加上108 → 998244294+108=998244402 → mod MOD → 998244402-998244353=49. 所以 sum_r=49. ans=26+49=75. 这与样例的输出一致。所以这个算法是正确的。 现在,考虑代码的时间复杂度。预处理组合数是O(K&sup2;)。预处理P数组是O(N). 预处理pow_P是O(NK). 预处理pre_sum是O(NK). 然后主循环是O(NK). 所以总的时间复杂度是O(NK + K&sup2;),对于N=2e5K=10来说,2e5*10=2e6,完全可以通过。 空间复杂度面,需要存储pow_P数组,是O(NK),对于N=2e5K=10来说是2e6,可以接受。同样,pre_sum数组是O(K*N),同样可行。 现在,将这些步骤转化为C++代码: 注意MOD=998244353. 预处理组合数: 初始化一个二维数组comb,大小(K+1)x(K+1). 然后,处理每个n从0到K,每个k从0到n. 对于本题,只需要K的情况,所以可以优化空间,但为了便,可能直接计算二维数组。 然后,前缀数组P: vector<long long> P(N+1, 0); pow_P数组: vector<vector<long long>> pow_P(N+1, vector<long long>(K+1)); pre_sum数组: vector<vector<long long>> pre_sum(K+1, vector<long long>(N+1, 0)); 然后,主循环: ans的初始化为0,然后每个r从1到N,计算sum_r并累加。 现在,C++代码的大致结构如下: #include <bits/stdc++.h> using namespace std; const int MOD = 998244353; int main() { int N, K; cin >> N >> K; vector<int> A(N); for (int i=0; i<N; ++i) { cin >> A[i]; } // 预处理组合数 vector<vector<long long>> comb(K+1, vector<long long>(K+1, 0)); comb[0][0] =1; for (int n=1; n<=K; ++n) { comb[n][0] =1; comb[n][n] =1; for (int t=1; t<n; ++t) { comb[n][t] = (comb[n-1][t-1] + comb[n-1][t]) % MOD; } } // 预处理前缀P vector<long long> P(N+1, 0); for (int i=1; i<=N; ++i) { P[i] = (P[i-1] + A[i-1]) % MOD; } // 预处理pow_P vector<vector<long long>> pow_P(N+1, vector<long long>(K+1)); for (int i=0; i<=N; ++i) { pow_P[i][0] =1; for (int m=1; m<=K; ++m) { pow_P[i][m] = pow_P[i][m-1] * P[i] % MOD; } } // 预处理pre_sum vector<vector<long long>> pre_sum(K+1, vector<long long>(N+1, 0)); for (int m=0; m<=K; ++m) { for (int r=1; r<=N+1; ++r) { // pre_sum[m][r] = sum_{x=0}^{r-1} pow_P[x][m} // 但是当r-1超过N时,会有问题。例如,r的范围是1到N+1? // 原题中的前缀数组的r是到N,所以x的范围是0到N. // pre_sum的r最大是N+1? // 例如,当r=N+1时,sum是x=0到N的pow_P[x][m}。 // 所以,当r的范围是1到N+1? // 可能之前的pre_sum的构造式需要调整循环变量。 // 原pre_sum[m][r]的计算: for (int r=1; r <= N+1; ++r) { if (r-1 <0) continue; pre_sum[m][r] = (pre_sum[m][r-1] + ( (r-1 <=N) ? pow_P[r-1][m] : 0 )) % MOD; } // 或者,正确的循环是: for (int r=1; r <= N+1; ++r) { if (r-1 > N) { pre_sum[m][r] = pre_sum[m][r-1]; // 后面的元素不存在,所以sum不变 } else { pre_sum[m][r] = (pre_sum[m][r-1] + pow_P[r-1][m]) % MOD; } } // 这似乎会导致数组越界。因此,原pre_sum的构造式可能需要r循环到N,而不是N+1? // 可能原代码中的pre_sum数组的r范围是1到N+1? // 例如,pre_sum[m][r]表示前r-1个元素的,所以当r-1的范围是0到N(当r从1到 N+1时),所以原循环中的r应该从1到 N+1. // 所以,pre_sum数组的大小是 K+1 x (N+2). 或者,可能我的初始代码构造pre_sum的式错误,导致当r超过N时无法处理。例如,当处理到r=N+1时,pow_P[r-1][m] 即 pow_P[N][m}是否存在? P数组的长度是N+1,索引从0到N。所以,r的范围是1到N+1时,r-1的范围是0到N,所以 pow_P[r-1][m}是存在的。例如,当r=N+1时,r-1 =N,是有效的索引。 所以,pre_sum的构造循环应该是: for (int m=0; m<=K; ++m) { pre_sum[m][0] =0; for (int r=1; r<=N+1; ++r) { // 因为当r= N+1时,sum是x=0到N. pre_sum[m][r] = (pre_sum[m][r-1] + pow_P[r-1][m}) % MOD; } } 这可能导致pre_sum数组的大小为K+1 x (N+2). 例如,原问题中的pre_sum[m][r]的r可以是1到N+1。例如,当r=3,N=3时,r-1=2是有效的索引。 这样,在处理r=3时,pre_sum[m][3]是sum x=0到2的pow_P[x][m}. 所以,原pre_sum构造的代码可能应该: pre_sum[m][0] =0; for (int r=1; r<=N+1; r++) { pre_sum[m][r] = (pre_sum[m][r-1] + pow_P[r-1][m]) % MOD; } 但此时,pre_sum数组的第二维需要是N+2的大小。例如,当N=3时,r可以取到4,所以数组的大小是K+1 × (N+2). 这在代码中需要将pre_sum初始化为: vector<vector<long long>> pre_sum(K+1, vector<long long>(N+2, 0)); 否则,在r循环到N+1时,pre_sum的索引会超出范围,导致错误。 所以,在代码中,pre_sum的构造应该: pre_sum的初始化应该是每个m的数组大小为N+2: pre_sum.resize(K+1, vector<long long>(N+2, 0)); 或者,在初始化时: vector<vector<long long>> pre_sum(K+1, vector<long long>(N+2, 0)); 然后,循环: for (int m=0; m <= K; ++m) { pre_sum[m][0] =0; for (int r=1; r <= N+1; ++r) { pre_sum[m][r] = (pre_sum[m][r-1] + pow_P[r-1][m]) % MOD; } } 这样,当处理到r= N+1时,r-1 =N,即sum x=0到N的pow_P[x][m},但原问题中是否需要这个? 因为在主循环中,r的范围是1到N(每个子数组的结束位置是1到N),所以当处理到r=N时,pre_sum[m][r]需要是sum x=0到r-1= N-1的pow_P[x][m}。所以,原来的pre_sum的构造可能不需要到N+1,而是到N即可。例如,原来的r循环到N即可,pre_sum[m][r]是sum x=0到r-1. 例如,原来的pre_sum构造代码: for (int m=0; m <= K; ++m) { pre_sum[m][0] =0; for (int r=1; r <= N; ++r) { pre_sum[m][r] = (pre_sum[m][r-1] + pow_P[r-1][m}) % MOD; } } 这样,pre_sum[m][r]是sum x=0到r-1的pow_P[x][m},其中r的范围是1到N. 此时,当处理到r=N时,pre_sum[m][r]是sum x=0到N-1的pow_P[x][m}. 但是,主循环中的r是1到N,对应的子数组结束位置是r,此时需要sum x=0到r-1的pow_P[x}^{K-t},也就是pre_sum[K-t][r}. 例如,当r=N时,x的范围是0到N-1. 这是正确的,因为子数组是P[r] - P[x},其中x的范围是0到 r-1. 因此,pre_sum的构造应该循环到r=N. 所以,正确的pre_sum构造循环是: for (int m=0; m <= K; ++m) { pre_sum[m][0] =0; for (int r=1; r <= N; ++r) { pre_sum[m][r] = (pre_sum[m][r-1] + pow_P[r-1][m]) % MOD; } } 这似乎更合理。因为当r=N时,pre_sum[m][r} = sum_{x=0}^{N-1} pow_P[x][m}。而当处理到r=N时,主循环中的r是N,所以对应的x的范围是0到N-1. 这样,pre_sum数组的构造是正确的。 现在,回到样例输入1中的pre_sum构造: 当m=2时,pre_sum[2][r=3] 是sum x=0到2的pow_P[x][2}。即: x=0:0^2=0 x=1:3^2=9 x=2:4^2=16 → 总25. 所以,pre_sum[2][3} =0+9+16=25. 这样,当处理r=3时,pre_sum[m=2][3}就是正确的25. 综上,代码中的pre_sum构造循环是正确的,当r循环到N时,pre_sum[m][r}是sum x=0到r-1. 现在,回到主循环中的处理: 对于每个r in 1..N: sum_r的计算涉及到pre_sum[K-t][r},即对于当前的r,pre_sum[m][r}的值是sum x=0到r-1的pow_P[x}^{m},其中m=K-t. 例如,当处理r=3时,pre_sum[m][3}就是sum x=0到2的pow_P[x}^{m}. 这与之前样例中的情况一致。 现在,编写完整的代码: #include <bits/stdc++.h> using namespace std; const int MOD = 998244353; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int N, K; cin >> N >> K; vector<int> A(N); for (int i=0; i<N; ++i) { cin >> A[i]; A[i] %= MOD; } // 预处理组合数 vector<vector<long long>> comb(K+1, vector<long long>(K+1, 0)); comb[0][0] = 1; for (int n=1; n<=K; ++n) { comb[n][0] = 1; comb[n][n] = 1; for (int t=1; t<n; ++t) { comb[n][t] = (comb[n-1][t-1] + comb[n-1][t]) % MOD; } } // 预处理前缀P vector<long long> P(N+1, 0); for (int i=1; i<=N; ++i) { P[i] = (P[i-1] + A[i-1]) % MOD; } // 预处理pow_P vector<vector<long long>> pow_P(N+1, vector<long long>(K+1, 0)); for (int i=0; i<=N; ++i) { pow_P[i][0] = 1; for (int m=1; m<=K; ++m) { pow_P[i][m] = pow_P[i][m-1] * P[i] % MOD; } } // 预处理pre_sum vector<vector<long long>> pre_sum(K+1, vector<long long>(N+1, 0)); for (int m=0; m<=K; ++m) { for (int r=1; r<=N; ++r) { pre_sum[m][r] = (pre_sum[m][r-1] + pow_P[r-1][m]) % MOD; } } long long ans = 0; for (int r=1; r<=N; ++r) { long long sum_r = 0; for (int t=0; t<=K; ++t) { int m = K - t; long long c = comb[K][t]; int s = (K - t) % 2; long long sign = (s == 0) ? 1 : MOD - 1; long long term = c * sign % MOD; term = term * pow_P[r][t] % MOD; term = term * pre_sum[m][r] % MOD; sum_r = (sum_r + term) % MOD; } ans = (ans + sum_r) % MOD; } ans = (ans % MOD + MOD) % MOD; cout << ans << endl; return 0; } 这个代码应该正确处理所有的测试用例。例如,对于第二个样例: Sample Input 2: 1 10 0 那么,A数组是[0]. P数组是: P[0]=0 P[1]=0. pow_P[0][m}:所有m都是0,除了m=0是1. pow_P[1][m}:所有m都是0,除了m=0是1. pre_sum[m][r=1}:对于所有m,pre_sum[m][1}= pow_P[0][m}。当m>0时,pow_P[0][m}是0^m=0.所以pre_sum[m][1}=0. 主循环处理r=1: sum_r是各个t的总: t从0到10: m=10-t. 例如,当t=10: m=0. sign=(-1)^(10-10)=1. c=comb(10,10)=1. term=1*1 * pow_P[1][10} * pre_sum[0][1} → pow_P[1][10}是0^10=0. 所以 term=0. 同理,所有t的情况下的term都是0。所以 sum_r=0,ans=0. 与样例输出一致。 第三个样例可能需要较长的计算时间,但代码的时间复杂度是允许的。 综上,这个代码应该正确解决问题。</think>```cpp #include <bits/stdc++.h> using namespace std; const int MOD = 998244353; int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int N, K; cin >> N >> K; vector<int> A(N); for (int i = 0; i < N; ++i) { cin >> A[i]; A[i] %= MOD; } // 预处理组合数 vector<vector<long long>> comb(K + 1, vector<long long>(K + 1, 0)); comb[0][0] = 1; for (int n = 1; n <= K; ++n) { comb[n][0] = 1; comb[n][n] = 1; for (int t = 1; t < n; ++t) { comb[n][t] = (comb[n-1][t-1] + comb[n-1][t]) % MOD; } } // 前缀数组 vector<long long> P(N + 1, 0); for (int i = 1; i <= N; ++i) { P[i] = (P[i-1] + A[i-1]) % MOD; } // 预处理幂次数组 vector<vector<long long>> pow_P(N + 1, vector<long long>(K + 1, 0)); for (int i = 0; i <= N; ++i) { pow_P[i][0] = 1; for (int m = 1; m <= K; ++m) { pow_P[i][m] = pow_P[i][m-1] * P[i] % MOD; } } // 预处理前缀的后缀 vector<vector<long long>> pre_sum(K + 1, vector<long long>(N + 1, 0)); for (int m = 0; m <= K; ++m) { for (int r = 1; r <= N; ++r) { pre_sum[m][r] = (pre_sum[m][r-1] + pow_P[r-1][m]) % MOD; } } long long ans = 0; for (int r = 1; r <= N; ++r) { long long sum_r = 0; for (int t = 0; t <= K; ++t) { int m = K - t; long long c = comb[K][t]; int s = (K - t) % 2; long long sign = (s == 0) ? 1 : MOD - 1; long long term = c * sign % MOD; term = term * pow_P[r][t] % MOD; term = term * pre_sum[m][r] % MOD; sum_r = (sum_r + term) % MOD; } ans = (ans + sum_r) % MOD; } ans = (ans % MOD + MOD) % MOD; cout << ans << '\n'; return 0; } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值