Summing Pieces
题意
有 N(1≤N≤1e6) 个数,分隔成数个子序列,总共有 2N−1 种分隔方法,求所有分隔中的每个数加起来乘以长度的和的和,比如 B=[(1,2,4),(5,1),(2)]=>(1+2+4)×3+(5+1)×2+(2)×1=35
解法
对于这种题型我的常用解法都是枚举每个数对总的结果的贡献,加起来就好了。
咱先定义 2−1=1
有 n 个数对于一个数ap ,在某种间隔序列时,它所在序列左边有 L 个右边有R 个数,此时它所在的子序列长度为 n−L−R ,这种序列有 2L−1×2R−1 种,这时候对总结果的贡献就为 2L−1×2R−1×(n−L−R)×ap ,所以对于数 ap(1≤p≤n) 对总结果的总贡献为所有合法的 L,R 带入上式得到的结果,就是:==∑i=0p−1∑j=0n−p2i−1∗2j−1∗(n−i−j)∗ap∑i=0p−1∑j=0n−p2i−1∗2j−1∗n−∑i=0p−1∑j=0n−p2i−1∗2j−1∗i−∑i=0p−1∑j=0n−p2i−1∗2j−1∗j∑i=0p−12i−1∗n∗∑j=0n−p2j−1−…
所以我们需要先预处理出 2i−1和2i−1×i 的前缀和, 2i−1×n 不用预处理.每个数的贡献就可以在 O(1) 的时间内算出来。
代码中我用 ci表示2i−1,sci前缀和,cii表示2i−1×i,scii 前缀和Code:
#include <bits/stdc++.h>
typedef long long LLONG;
const int N = 1e6 + 10;
const LLONG MOD = 1e9 + 7;
inline void tryMulti(LLONG& o, LLONG mul) {
if(o >= MOD) {
o %= MOD;
}
o *= mul;
if(o >= MOD) {
o %= MOD;
}
}
inline void tryAdd(LLONG& o, LLONG s) {
o += s;
if(o < 0) {
o %= MOD;
o += MOD;
}
if(o >= MOD) {
o %= MOD;
}
}
LLONG c[N], ci[N];
LLONG sc[N], sci[N];
inline void initial(int n) {
sc[0] = c[0] = 1;
sci[0] = ci[0] = 0;
LLONG cur = 1;
for(int i = 1; i <= n; ++i) {
ci[i] = c[i] = cur;
tryMulti(ci[i], i);
sc[i] = c[i];
sci[i] = ci[i];
tryAdd(sc[i], sc[i - 1]);
tryAdd(sci[i], sci[i - 1]);
tryMulti(cur, 2);
}
}
inline LLONG compute(int x, int l, int r) {
LLONG tmp = sc[l];
tryMulti(tmp, sc[r]);
tryMulti(tmp, l + r + 1);
LLONG rlt = tmp;
tmp = sci[l];
tryMulti(tmp, sc[r]);
tryAdd(rlt, -tmp);
tmp = sci[r];
tryMulti(tmp, sc[l]);
tryAdd(rlt, -tmp);
tryMulti(rlt, x);
return rlt;
}
int main() {
int n;
scanf("%d", &n);
initial(n);
LLONG rlt = 0;
for(int i = 1; i <= n; ++i) {
int x;
scanf("%d", &x);
tryAdd(rlt, compute(x, i - 1, n - i));
}
std::cout << rlt % MOD << std::endl;
return 0;
}