NOIP2015普及组★求和★数学

来源:互联网 发布:趋势科技杀毒软件 知乎 编辑:程序博客网 时间:2024/05/21 19:45

  • 题目
    • 题目描述
    • 输入
    • 输出
    • 输入样例1
    • 输出样例1
    • 输入样例2
    • 输出样例2
    • 提示
      • 输入输出样例 1 说明
      • 数据规模
  • 思路
    • 概述
    • 推导
    • 优化
  • 代码

题目

题目描述

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

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

  • x,y,z是整数
  • x < y < z
  • y-x = z-y
  • color_x=color_z

满足上述条件的三元组的分数规定为(x+z)*(number_x+number_z)。整个纸带的分数规定为所有满足条件的三元组的分数的和。这个分数可能会很大,你只要输出整个纸带的分数除以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

思路

概述

看到这道题,我不由自主地想到了2016年的魔法阵= =
它的条件肯定是有心机的,所以一条一条地看:

  • x,y,z是整数
    这个没什么好说的。
  • x < y < z
    这个也没什么好说的。
  • y-x = z-y
    移项,得z+x=2y
    2y为偶数,z+x等于一个偶数,zx又都为整数,说明xz同奇同偶
  • color_x=color_z
    xz颜色一样。

好像并没有分析出什么,还发现y没有什么用……

但是,对于1-N所有的编号,只要这个编号满足这样的条件,都可以作为一个三元组,所以完全可以把满足上述条件的编号存在一起(暂且先存编号,接下来的推导会让你发现不需要存编号),即是把同奇偶,同颜色的存在一起。

至于y,直接扔掉。

别忘了还有传说中分数的计算方式:

  • (x+z)×(numberx+numberz)

numberx简写为nx,果断拆开它:
xnx+xnz+znx+znz

推导

几个好像看不出什么规律,我们可以多写几个:
N个编号a1,a2,...,aN,满足颜色全部相同,且同为奇或偶,
则总分数为:
(a1+a2)(n1+n2)+(a1+a3)(n1+n3)+(a1+a4)(n1+n4)+...+(a1+aN)(n1+nN)+
(a2+a3)(n2+n3)+(a2+a4)(n2+n4)+(a2+a5)(n2+n5)+...+(a2+aN)(n2+nN)+
(a3+a4)(n3+n4)+(a3+a5)(n3+n5)+(a3+a6)(n3+n6)...+(a3+aN)(n3+nN)+
......+
(aN2+aN1)(nN2+nN1)+(aN2+aN)(nN2+nN)+
(aN1+aN)(nN1+nN)

拆开并化简一些得:
(N1)a1n1+(N2)a2n2+...+2aN2nN2+aN1nN1+
a1(n2+n3+...+nN)+a2(n3+n4+...+nN)+...+aN2(nN1+nN)+aN1nN+
n1(a2+a3+...+aN)+n2(a3+a4+...+aN)+...+nN2(aN1+aN)+nN1aN+
a2n2+2a3n3+...+(N3)aN2nN2+(N2)aN1nN1+(N1)aNnN
这是将每个2次式拆开,然后合并之前xnx+xnz+znx+znz中相同位置的项后得到的,例如第一行为所有的xnx这一项合并后得到的。

将第1行和第4行合并得:
(N1)a1n1+(N1)a2n2+...+(N1)aN2nN2+(N1)aN1nN1+(N1)aNnN+
a1(n2+n3+...+nN)+a2(n3+n4+...+nN)+...+aN2(nN1+nN)+aN1nN+
n1(a2+a3+...+aN)+n2(a3+a4+...+aN)+...+nN2(aN1+aN)+nN1aN

继续:
(N1)(a1n1+a2n2+...+aN2nN2+aN1nN1+aNnN)+
a1(n2+n3+...+nN)+a2(n3+n4+...+nN)+...+aN2(nN1+nN)+aN1nN+
n1(a2+a3+...+aN)+n2(a3+a4+...+aN)+...+nN2(aN1+aN)+nN1aN

事实上,最后一行换一种合并方式,提a而不提n,得:
(N1)(a1n1+a2n2+...+aN2nN2+aN1nN1+aNnN)+
a1(n2+n3+...+nN)+a2(n3+n4+...+nN)+...+aN2(nN1+nN)+aN1nN+
a2n1+a3(n1+n2)+...+aN2(n1+n2+...+nN3)+aN1(n1+n2+...+nN2)+aN(n1+n2+...+nN1)

于是,2、3行便可以合并了:
(N1)(a1n1+a2n2+...+aN2nN2+aN1nN1+aNnN)+
a1(n2+n3+...+nN)+a2(n1+n3+n4+...+nN)+...+aN1(n1+n2+...+nN2+nN)+aN(n1+n2+...+nN1)

发现第二行中的第i项中少一个aini,于是可以把这些aini放到第一行中,继续化简:
(N2)(a1n1+a2n2+...+aN1nN1+aNnN)+(a1+a2+...+aN)(n1+n2+...+nN)

来简化一下:
(N2)(aini)+aini

可能看上去十分繁琐,但是也不算太难,为了更容易看懂,我写得很详细(一中午的大好光阴,就消逝在这这些数学符号之中……)

优化

但是,储存每个似乎不大好,我们可以优化一下储存方式:
f[i][0]表示颜色为i,编号为偶数的格子的num之和
f[i][1]表示颜色为i,编号为奇数的格子的num之和
n[i][0]表示颜色为i,编号为偶数的格子数量
n[i][1]表示颜色为i,编号为奇数的格子数量
c[i]表示格子i的颜色
N[i]表示格子i上的数字

(N2)(a1n1+a2n2+...+aN1nN1+aNnN)+(a1+a2+...+aN)(n1+n2+...+nN)变一下:

枚举编号i
(N2)(iN[i])+(if[n[i][i%2]][i%2])
提一个i
(i((N2)N[i]+f[n[i][i%2]][i%2]))

完了~
记得随时mod哦~

代码

#include<cstdio>int read(){    int x=0,f=1;char s=getchar();    while(s<'0'||s>'9'){if(s=='-')f=-1;s=getchar();}    while(s>='0'&&s<='9'){x=x*10+s-'0';s=getchar();}    return x*f;}#define MAXN 100000#define MAXM 100000#define MOD 10007int N,ans;int f[MAXM+5][2],n[MAXM+5][2];int Number[MAXN+5],Color[MAXN+5];int main(){    N=read();    read();    for(int i=1;i<=N;i++)        Number[i]=read()%MOD;    for(int i=1;i<=N;i++)    {        Color[i]=read();        f[Color[i]][i&1]=(f[Color[i]][i&1]+Number[i])%MOD;        n[Color[i]][i&1]++;    }    for(int i=1;i<=N;i++)        ans=(ans+(((n[Color[i]][i&1]-2)%MOD*Number[i]%MOD+f[Color[i]][i&1])%MOD)*(i%MOD)%MOD)%MOD;    printf("%d",ans);}