【CCPC】2022桂林站 C. Array Concatenation | 数学

276 阅读2分钟

本文已参与「新人创作礼」活动, 一起开启掘金创作之路。

题目链接

Problem - C - Codeforces

题目

Little relyt871 has a magical machine. In each operation, his machine can do one of the following operations to the input array bb:

  • Generate a copy of bb and concatenate it after bb. More formally, the resulting array should be
b={b1,b2,,bb,b1,b2,,bb}b′=\{b_1,b_2,…,b_{|b|},b_1,b_2,…,b_{|b|}\}
  • Generate a copy of bb, reverse it, then concatenate it before bb. More formally, the resulting array should be
b={bb,bb1,,b1,b1,b2,,bb}b′=\{b_{|b|},b_{|b−1|},…,b_1,b_1,b_2,…,b_{|b|}\}

Initially, he has an array aa of length nn. Then, he wants to operate the machine exactly mm times using the array on his hand while maximizing the sum of all prefix sums of the final array. Since he has a somewhat finite brain, when he adds some integers, he only cares about the sum modulo 1000000007. Formally, suppose after all mm operations he has array bb of length nn′, he wants to maximize the following value:

(i=1nj=1ibj)(mod1000000007)(\sum_{i=1}^{n′}\sum_{j=1}^{i}b_j)(\mod1000000007)

Please note that you should maximize the value after taking the modulo: the array with answer 1000000007 before taking the modulo is considered less than the array with answer 1.

题目大意

给定一个长度为 nn 的数组 b={b1,b2,,bn}b=\{b_1,b_2,…,b_n\},每次操作可以选择一下两种之一:

  1. 复制自己接在后面,原数组变成 b={b1,b2,,bb,b1,b2,,bb}b′=\{b_1,b_2,…,b_{|b|},b_1,b_2,…,b_{|b|}\}
  2. 复制自己 reverse 之后放在前边,原数组变成 b={bb,bb1,,b1,b1,b2,,bb}b′=\{b_{|b|},b_{|b−1|},…,b_1,b_1,b_2,…,b_{|b|}\}

执行恰好k次操作,求

(i=1nj=1ibj)(mod1000000007)(\sum_{i=1}^{n′}\sum_{j=1}^{i}b_j)(\mod1000000007)

的最大值。

思路

因为一个普通串执行过操作 2 后会变成回文串,回文串 A 不论执行操作 1 还是操作 2 都会变成 AA,并且还是回文串。即执行过 reverse 操作后的串再执行两种操作产生的效果相同,可以视为只能执行一次 reverse 操作。

观察题目中的式子,

(i=1nj=1ibj)(mod1000000007)(\sum_{i=1}^{n′}\sum_{j=1}^{i}b_j)(\mod1000000007)

显然等价于

(i=1nbi×(ni+1)(mod1000000007)(\sum_{i=1}^{n′}b_i\times (n′-i+1)(\mod1000000007)

如果最初的数组中的值是 b1,b2,b3,b4{b_1,b_2,b_3,b_4},统计最初的数组中每个元素应该乘的系数:

  • 只使用操作 1 得到的串是 {b1,b2,b3,b4,b1,b2,b3,b4,...}\{b_1,b_2,b_3,b_4,b_1,b_2,b_3,b_4,...\},每 nn 个相对位置不变,相邻两组差 nn,共 2m2^m 组。那么原数组第 ii 项应该乘的系数就是
((ni+1)+0×n)+((ni+1)+1×n)+...+((ni+1)+(2m1)×n)(ni+1)×2m+2m×(2m1)2×n((n-i+1)+0\times n)+((n-i+1)+1\times n)+...+((n-i+1)+(2^{m}-1)\times n)\\ (n-i+1)\times 2^m+\frac{2^m\times(2^m-1)}{2}\times n
  • 假设第 jj 次操作使用了操作 2,则出现在 ii 上的元素也一定出现在 ni+1n-i+1 上。所以每个元素在第 jj 次操作后应该乘的系数是
(n+1)2j1+(2j1)2j2×n(n+1)*2^{j-1}+\frac{(2^j-1)*2^j}{2}\times n

还剩 kjk-j 次操作,每次操作把整个串复制一份,每 2j2^j 个相对位置不变,相邻两组差 nn,所以不论什么时候使用了操作 2,最终每个元素应该乘的系数都是

(n+1)2m1+2m×(2m1)2×n(n+1)*2^{m-1}+\frac{2^m\times(2^m-1)}{2}\times n

只需分别计算使用或不使用操作 2 的答案取最大值输出即可,注意多次取余防止越界。

代码

#include <stdio.h>
#include <algorithm>
using namespace std;
using LL=long long;
const LL mod=1e9+7;
LL a[100001];
LL poww(LL a,LL b)
{
	LL ans=1;
	for (;b;b>>=1,a=a*a%mod)
		if (b&1) ans=ans*a%mod;
	return ans;
}

int main()
{
	int n;
	LL m,ans1=0,ans2=0;
	scanf("%d%lld",&n,&m);
	LL k=poww(2,m-1);
	m=poww(2,m);
	for (int i=1;i<=n;++i)
	{
		scanf("%lld",&a[i]);
		ans1+=((n-i+1)*m%mod+(m-1)*m/2%mod*n%mod)%mod*a[i]%mod;
		ans2+=((n+1)*k%mod+(m-1)*m/2%mod*n%mod)%mod*a[i]%mod;
		ans1%=mod;
		ans2%=mod;
	}
	printf("%lld\n",max(ans1,ans2));
        return 0;
}