agc028 B Removing Blocks¶
题面¶
给出一个序列,每个点都有一个权值,一开始他们都是连通的。
Snuke将执行以下操作N次:
- 选择一个点并将其移除,移除这个点所用的代价为此时他所在的块内所有点的权值和(包括自己),将这个点移除之后原本所在的块会因为其被删除而被分成两个块。
对于所有可能的 N! 个选择方案,算出他们的代价总和。
比如:
Input
2
1 2
Output
9
题解¶
我在比赛的时候没有写出来这一题,第二天看题解,感觉还是非常神奇的。
我们忽略选择顺序,来算一个选择方案中的所有代价的总和的期望。
考虑单点对整体算贡献的方法,我们考虑一个点,其会被包括(也就是有贡献)的期望概率。
假设删除某个点 i 时,点 j 和它在同一个块中的概率为 P(i,j) ,那么如果要满足此时两点在同一个块,就需要选择 i 时:i,i+1,\cdots ,j 都没有被选择过。
那么概率其实就是 1/(|j-i|+1),就是i~j中只选则到i的概率。
我们设b[j]=\sum_{i=1}^{N} P(i,j):
然后一个点 i 的期望贡献就是 a[i]*b[i]
最后将所有点的期望值相加 \times N! 就是答案。
可以通过预处理 1/1,1/2,1/3,\cdots ,1/N 的前缀和(模10^9+7意义下)做到时间复杂度O(N)。
代码¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | #include <bits/stdc++.h> #define MAX_N 100007 #define MOD 1000000007 using namespace std; typedef long long ll; int N; ll a[MAX_N], b[MAX_N]; ll inv[MAX_N], sum[MAX_N], res; inline void init () { inv[0] = inv[1] = 1; for (int i = 2;i <= N; ++i) inv[i] = inv[MOD % i] * (MOD - MOD / i) % MOD; for (int i = 1;i <= N; ++i) sum[i] = sum[i - 1] + inv[i]; } int main () { scanf("%d", &N); for (int i = 1;i <= N; ++i) scanf("%d", a + i); init(); for (int i = 1;i <= N; ++i) b[i] = (sum[N - i + 1] - 1 + sum[i] + MOD) % MOD; for (int i = 1;i <= N; ++i) (res += a[i] * b[i]) %= MOD; for (int i = 2;i <= N; ++i) res = res * i % MOD; printf("%lld\n", res % MOD); return 0; } |