P2671 求和 题解
Victorique_De_Blois
2019-01-07 13:44:39
令$Score_i$表示以$i$为三元组第一个数的得分。
让我们来看看得分公式:
$$Score=(x+z)\cdot (number_x+number_z)$$
由乘法分配率可知:
$$Score=x\cdot number_x+x\cdot number_z+z\cdot number_x+z\cdot number_z$$
我们设可以以$x,y$的形式出现在同一三元组里的集合$\{i\}$
可得集合$\{i\}$的得分为:
$$(i1\cdot num[i1]+i1\cdot num[i2]+i2\cdot num[i1]+i2\cdot num[i3])+(i1\cdot num[i1]+i1\cdot num[i3]+i3\cdot num[i1]+i3\cdot num[i3])+...$$
整理可得:
$$Score_{i_n}=i_n\cdot \sum_{d=1}^nnumber_{i_d}+(n-2)\cdot(i_n\cdot number_{i_n})$$
我们把$i_n$提出来,就得到了如下公式:
$$Score_{i_n}=i_n\cdot[\sum_{d=1}^nnumber_{i_d}+(n-2)\cdot number_{i_n}]$$
再看题目要求:
这个分数可能会很大,你只要输出整个纸带的分数**除以**10,007所得的**余数**即可。
所以我们应该利用同余定理改写这个公式:
记住下面这个恒等式:
$$(\sum_{i=1}^n a_i)(mod\ M)≡\sum_{i=1}^n[a_i (mod\ m)]$$
设$m=10007$,我们有:
$$Score_{i_n}(mod\ m)=i_n(mod\ m)\cdot\{\sum_{d=1}^nnumber_{i_d}+[(n-2)(mod\ m)\cdot number_{i_n}+m](mod\ m)\}$$
由题意得:
$$ans=\sum_{i=1}^nScore_i$$
我们利用前缀和优化这个公式,可将时辅优化到$O(n)$
可能你对集合$\{i\}$的定义还不是很清楚。
特殊三元组$(x,y,z)$满足以下性质:
由题意得:
- $x<y<z$
- $z-y=y-x$
- $color_x=color_z$
为了方便,我们设$y=x+k$则$z=y+k$
则有:
$$2y=x+z→(x+y)(mod\ 2)=0$$
~~那该三元组和y还有什么关系~~
所以我们只要满足$x,z$同奇偶,颜色相同,便确认他们同在集合$\{i\}$
前缀和优化如下:
设$sum[color_i][g]$表示同为颜色$color_i$,$(mod\ 2)=g$的所有数的数字和,即公式中的$\sum_{d=1}^nnumber_{i_d}$
再设$nt[color_i][g]$表示这个集合的个数,即公式中的$n$
一边输入颜色,就可一边累加,然后你就可以AC掉本题了。
完整代码如下:
#include <cstdio>
const int N = 100000;
const int M = 10007;
int n, m;
int sum[N + 1][2], nt[N + 1][2];
int color[N + 1], number[N + 1];
long long ans = 0;
int main()
{
scanf(" %d %d", &n, &m);
for(int i = 1; i <= n; i++) {
scanf("%d", &number[i]);
number[i] %= M;
}
for(int i = 1; i <= n; i++) {
scanf("%d", &color[i]);
int c = color[i];
int g = i % 2;
nt[c][g]++;
sum[c][g] += number[i];
sum[c][g] %= M;
}
for(int i = 1; i <= n; i++) {
int c = color[i];
int g = i % 2;
ans += i % M * ((sum[c][g] + (nt[c][g] - 2) % M * number[i] + M) % M);
ans %= M;
}
printf("%lld", ans);
return 0;
}