P2671 [NOIP 2015 普及组] 求和
好题,思想很好。
首先看到这个题一个显然的思路是\(O(n^3)\)的暴力,直接枚举三个判断可行性计算贡献。
思考简单的优化,题目条件限制z-y=y-x
变形可得\(y=\frac{z-x}{2}\),由于\(y\)一定是正整数,所以\(z\)与\(x\)正负性相同,考虑将原数组拆分为奇数和偶数两个集合,并在两个集合中分别枚举,复杂度下降到\(O(n^2)\)。
再次思考优化,将题目中计算贡献式子拆开:
\[(x+z) \times (a_x+a_z) = xa_x+xa_z+za_x+za_z
\]
考虑问题变为,如何对于同样奇偶性下的每一对\((x,z)\)快速计算这个贡献呢?
注意,以下说到的均为奇偶性相同且同颜色的情况计算,对于这两种情况并不难分开处理,采用空间换时间的思想开两个数组即可。
不妨简化问题,针对n=5
的情况进行判断,对于一个数列a_1,a_2,...,a_n
,所有的贡献是这样的:
考察图中所有系数是\(1\)的项,发现最终答案是\((n-1)\times 1\times a_i+1\times (\sum_{j=1}^{n}a_j - a_1)\),手摸其他系数可以发现规律,对于\(i\)这一位的贡献为:
\[(n-1)\times i\times a_i + i\times (\sum_{j=1}^{n}a_j - a_i)
\]
接下来就简单了,考虑\(s1\)二维数组记录某种颜色且编号为奇数或者偶数的格子数,\(s2\)记录某种颜色且编号为奇数或偶数的数字和,简单维护即可,对于每个\(i\)的答案就是i*((s2[y][i%2]-a[i])+a[i]*(s1[y][i%2]))
,把全部的\(\sum\)起来就好了。
代码:
# include <bits/stdc++.h>
using namespace std;
const int N = 1e5+10;
const int mod = 1e4+7;
int num[N],col[N];
int s1[N][2],s2[N][2];
int main (){
int n,m;
scanf("%d%d",&n,&m);
for(int i = 1;i <= n;i++) scanf("%d",&num[i]);
for(int i = 1;i <= n;i++)
{
scanf("%d",&col[i]);
s1[col[i]][i%2]++;
s2[col[i]][i%2] = (s2[col[i]][i%2]+num[i])%mod;
}
long long ans = 0;
for(int i = 1;i <= n;i++)
{
ans += i*(s2[col[i]][i%2]+num[i]*(s1[col[i]][i%2]-2)%mod)%mod;
// ans = (ans+(i*((s2[col[i]][i%2]-num[i]+mod)%mod+(s1[col[i]][i%2]-1)*num[i]))%mod)%mod;
ans %= mod;
}
cout << ans << endl;
return 0;
}