Hackerrank World CodeSprint 7
Summing Pieces
传送门
题目大意:给你一个数列 把这个数列分成若干个子串 求出所有分法的权值和(权值和的计算方式为:Σ每个子串的长度*每个子串的权值和)(n<=10^6)
首先想到了一个n^2的dp
但是后来不会优化…
于是就想到了贡献法
下面是做法:
一个长度为i的数列的分法为2 ^ (i-1)种
对于位于p的位置的数,假设他左边有L个数,右边有R个数,包含它的子串的长度为n - L - R,
那么他的对答案的贡献为
但是计算一次显然是O(n^2)的 我们需要把它优化到O(1)
我们把这个式子化简一下:
我们假设g(i) = 2 ^ (i-1)
那么我们只要预处理g(i) 和 g(i) * i的前缀和 就可以做到O(1)的转移了
代码如下:
#include<cstdio>
#include<iostream>
using namespace std;
const long long mod=1000000007;
const int N=1000010;
long long ans;
int n;
long long sum1[N],a[N],sum2[N],g[N],gi[N];
int main()
{
scanf("%d",&n);
for (int i=1;i<=n;i++)
scanf("%d",&a[i]);
g[0]=1;sum1[0]=1;g[1]=1;sum1[1]=1;gi[1]=1;
for (int i=2;i<=n;i++)
{
g[i]=g[i-1]*2%mod;
gi[i]=g[i]*i%mod;
}
for (int i=1;i<=n;i++)
{
sum1[i]=(sum1[i-1]+g[i])%mod;
sum2[i]=(sum2[i-1]+gi[i])%mod;
}
// for (int i=0;i<=n;i++) cout<<sum1[i]<<" "<<sum2[i]<<endl;
for (int i=1;i<=n;i++)
{
long long sum=0;
sum=(sum+((sum1[i-1]*sum1[n-i])%mod*n)%mod)%mod;
sum=(sum+mod-(sum2[i-1]*sum1[n-i])%mod)%mod;
sum=(sum+mod-(sum2[n-i]*sum1[i-1])%mod)%mod;
ans=(ans+(sum*a[i])%mod)%mod;
// cout<<ans<<endl;
}
printf("%d\n",ans);
}