[NOIP2015普及组]求和

时间:2023-03-10 05:01:40
[NOIP2015普及组]求和

题目

题目描述

一条狭长的纸带被均匀划分出了n个格子,格子编号从1到n。每个格子上都染了一种颜色color_i用[1,m]当中的一个整数表示),并且写了一个数字numberi。

[NOIP2015普及组]求和

定义一种特殊的三元组:(x,y,z),其中x,y,z都代表纸带上格子的编号,这里的三元组要求满足以下两个条件:

1. x,y,z是整数, x<y<z,y−x=z−y

2. colorx=colorz

满足上述条件的三元组的分数规定为(x+z)∗(numberx+numberz)。整个纸带的分数规定为所有满足条件的三元组的分数的和。这个分数可能会很大,你只要输出整个纸带的分数除以10,007所得的余数即可。

输入输出格式

输入格式:

第一行是用一个空格隔开的两个正整数n和m,n表纸带上格子的个数,m表纸带上颜色的种类数。

第二行有n用空格隔开的正整数,第i数字number表纸带上编号为i格子上面写的数字。

第三行有n用空格隔开的正整数,第i数字color表纸带上编号为i格子染的颜色。

输出格式:

共一行,一个整数,表示所求的纸带分数除以10,007所得的余数。

输入输出样例

输入样例1:

6 2

5 5 3 2 2 2

2 2 1 1 2 1

输出样例1:

82

输入样例2:

15 4

5 10 8 2 2 2 9 9 7 7 5 6 4 2 4

2 2 3 3 4 3 3 2 4 4 4 4 1 1 1

输出样例2:

1388

说明

【输入输出样例 1 说明】

纸带如题目描述中的图所示。

所有满足条件的三元组为: (1, 3, 5), (4, 5, 6)。

所以纸带的分数为(1 + 5)(5 + 2) + (4 + 6)(2 + 2) = 42 + 40 = 82。

对于第 1 组至第 2 组数据, 1 ≤ n ≤ 100, 1 ≤ m ≤ 5;

对于第 3 组至第 4 组数据, 1 ≤ n ≤ 3000, 1 ≤ m ≤ 100;

对于第 5 组至第 6 组数据, 1 ≤ n ≤ 100000, 1 ≤ m ≤ 100000,且不存在出现次数超过 20 的颜色;

对 于 全 部 10 组 数 据 , 1 ≤ n ≤ 100000, 1 ≤ m ≤ 100000, 1 ≤ color_i ≤ m,1≤number_i≤100000

分析

20分做法

看完这题,第一想法当然是无脑暴力啦…直接枚举x,y,z,看是否满足条件即可。算法复杂度为O(N3)。[代码就算了]

这样就可得20分了。当然,如果你想用更高级的算法不开long long也是可以的。

40分做法

可以直接枚举x,z的值,通过条件(1)求出y。再看是否满足条件。算法复杂度为O(N2)。

40~50分做法

仍然是枚举x,z的值,但可以先分析x+z=2*y的奇偶性,因为xyz是整数,因此2y是2的倍数,因此x,z必然都为偶数或奇数,因此可以分奇偶性进行枚举,此时这个三元组即可不考虑y值的大小,即为当(i%2==1)枚举前面奇数序列相同的颜色进行算分数即可,算法复杂度为O(N2/2)。

100分做法

通过上面的观察我们可以发现倒回去算前面的分数可能比较浪费时间,因此,我们采用数学的方法来优化此题(放心,很简单的)。

=============科目分割线==============

(这里只说明了i%2==1的情况,i%2==0是完全相似的)

令S(i,j)为(i,i+j/2,j)所组成的三元组的分数(x+z)∗(numberx+numberz)

Scorei为对于一个格子i的作为三元组第一个元素的总分数

colornum(colori)为对于一个格子i前方所有与之相同颜色且同奇偶性的格子的g个数

因此,对于一个格子K,有Scorek=S(1,k)+S(3,k)+...+S(colornum(colork,k−1),k)

=(1+k)∗(number1+numberk)+...+(colornum(colork)+k)∗(number(colornum(colork))+numberk)

=number1∗1+numberk∗1+number1∗k+numberk∗k+...+number(colornum(colork))∗colornum(colork)+number(colornum(colork))∗numberk+number(colornum(colork))∗k+numberk∗k

=(1∗number1+3∗number3+...+number(colornum(colork))∗colornum(colork))+k∗(number1+number3+...+number(colornum(colork)))+numberk∗(1+3+...+colornum(colork))+colornum(colork)∗k∗numberk

=============科目分割线==============

发现了什么,我们可以记录不同颜色的1∗number1+3∗number3+...+number(colornum(colork))∗colornum(colork),number1+number3+...+number(colornum(colork)),1+3+...+colornum(colork)的值,从而快速算出总分数。这样就可以轻易AK了~。算法复杂度O(n)

最后记得将结果不断%10007(是的,不断%%%%%%).

然后所有数都开long long 就可以过了~.

代码实现

我知道你们只看这个…

放个好理解的版本。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<climits>
#include<cmath>
using namespace std;
long long n,m,num[100005],cont[2][100005],cl;
long long sum1[3][100005],sum2[3][100005];
long long ans;
int main()
{
    scanf("%lld%lld",&n,&m);
    for(int i=1;i<=n;i++)scanf("%lld",&num[i]);
    for(int i=1;i<=n;i++)
    {
        scanf("%lld",&cl);
        if(i%2==1)
        {
            ans=(ans+sum1[0][cl]%10007+i*sum1[1][cl]%10007+num[i]*sum1[2][cl]%10007+cont[0][cl]*i*num[i]%10007)%10007;
            sum1[0][cl]=(sum1[0][cl]+num[i]*i)%10007;
            sum1[1][cl]=(sum1[1][cl]+num[i])%10007;
            sum1[2][cl]=(sum1[2][cl]+i)%10007;
            cont[0][cl]++;
        }
        else
        {
            ans=(ans+sum2[0][cl]%10007+i*sum2[1][cl]%10007+num[i]*sum2[2][cl]%10007+cont[1][cl]*i*num[i]%10007)%10007;
            sum2[0][cl]=(sum2[0][cl]+num[i]*i)%10007;
            sum2[1][cl]=(sum2[1][cl]+num[i])%10007;
            sum2[2][cl]=(sum2[2][cl]+i)%10007;
            cont[1][cl]++;
        }
    }
    printf("%lld",ans%10007);
}