◇NOIP2015普及组◇求和
Description
一条狭长的纸带被均匀划分出了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
colorx=colorz
满足上述条件的三元组的分数规定为
(x+z)∗(numberx+numberz)
。整个纸带的分数规定为所有满足条件的三元组的分数的和。这个分数可能会很大,你只要输出整个纸带的分数除以10007所得的余数即可。
Input
第一行是用一个空格隔开的两个正整数n和m,n表纸带上格子的个数,m表纸带上颜色的种类数。
第二行有n用空格隔开的正整数,第i数字number表纸带上编号为i格子上面写的数字。
第三行有n用空格隔开的正整数,第i数字color表纸带上编号为i格子染的颜色。
Output
共一行,一个整数,表示所求的纸带分数除以10,007所得的余数。
Sample Input
输入样例#1:
6 2
5 5 3 2 2 2
2 2 1 1 2 1
输入样例#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
Sample Output
输出样例#1:
82
输出样例#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≤colori≤m,1≤numberi≤100000
。
||题目解析||
从要求三元组的条件入手——移项得到
x+z=2y
,由于 x、y、z 都是整数,所以 x与z 必须同奇同偶(同奇同偶才能使它们的和为偶数)。因为
x<y<z
,所以只要 x、z 不越界,y 就一定不会越界。意思就是不用管 y 的值,那么我们就把原来的三元问题转换为了二元问题。正好,问题里的其他条件也只涉及 x 和 z ,那么我们的思路就是正解。
注意这是在同一种颜色中进行的,所以为了探寻其中规律,我们可以假设 集合A{
a1,a2,a3,...,an−1,an
} 为输入中某一同样颜色且同奇同偶的格子的编号,集合B{
b1,b2,b3,...,bn−1,bn
} 为输入中某一同样颜色且同奇同偶的格子标注的数字。即得与
a1
相关的项分数为:
(a1+a2)(b1+b2)+(a1+a3)(b1+b3)+...+(a1+an)(b1+bn)
=(n−1)a1b1+(a1b2+a1b3+...+a1bn)+(a2b1+a3b1+...+anb1)+(a2b2+a3b3+...+anbn)
=(n−1)a1b1+a1(b2+b3+...+bn)+b1(a2+a3+...+bn)+(a2b2+a3b3+...+anbn)
越写越像数论了……
再推,与
a2
相关的项分数为:
(a2+a3)(b2+b3)+(a2+a4)(b2+b4)+...+(a2+an)(b2+bn)
=(n−2)a2b2+(a2b3+a2b4+...a2bn)+(a3b2+a4b2+...anb2)+(a3b3+a4b3+...+anbn)
=(n−2)a2b2+a2(b3+b4+...+bn)+b2(a3+a4+...+an)+(a3b3+a4b4+...+anbn)
与 ai 相关的长这样….
(ai+ai+1)(bi+bi+1)+(ai+ai+2)(bi+bi+2)+...+(ai+an)(bi+bn)
=(n−i−1)aibi+(aibi+1+aibi+2+...+aibn)+(ai+1bi+ai+2bi+...+anbi)+(aibi+ai+1bi+1+...+anbn)
=(n−i−1)aibi+ai(bi+1+bi+2+...+bn)+bi(ai+1+ai+2+...+an)+(ai+1bi+1+ai+2bi+2+...+anbn)
整合一下,所有的分数应该是:
(n−1)(a1b1+a2b2+...+an−2bn−2+an−1bn−1+anbn)
+a1(b2+b3+...+bN)+a2(b3+b4+...+bn)+...+an−2(bn−1+bn)+an−1bn
+a2b1+a3(b1+b2)+...+an−2(b1+b2+...+bn−3)+an−1(b1+b2+...+bn−2)+an(b1+b2+...+bn−1)
再合并一下下…
(n−2)(a1b1+a2b2+...+an−1nn−1+anbn)+(a1+a2+...+an)(b1+b2+...+bN)
So…这么丑的通式何不用 “ ∑ ” 来简化一下?
(n−2)∑ni=1(aibi)+∑ni=1ai∑ni=1bi
推理完了…(陷入数学的深渊 ~ ~o(>_<)o ~ ~ )
(成功从数学转入编程…)
如何用程序实现这些公式?当然不能每一次都计算一遍它们的和!于是想到用屡试不爽的前缀和!
首先我们需要一个存结果的int数组(ans),结合之前提到的按颜色和奇偶性分类,应这样定义:int ans[Max_m+5][2]
,即ans[i][j] 表示颜色为 i 且奇偶性为 j (1为奇数,0为偶数)的数的和,同时需要一个对应ans的int数组len[i][j] 表示颜色为 i 且奇偶性为 j 的格子的个数。这两个数组都可以在读入颜色时算出来,别忘了取模。
最后定义一个int变量 Ans 存储答案。按照之前提到的通式求得Ans的值(最后水一下下)
||程序样例||
/*Lucky_Glass*/
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
int read() //读入优化走起
{
int x=0;char ch=getchar();
while(ch<'0' || ch>'9') ch=getchar();
while('0'<=ch && ch<='9') x=x*10+ch-'0',ch=getchar();
return x;
}
const int Mod=10007;
int num[100005],color[100005],ans[100005][2],len[100005][2];
int main()
{
int n=read(),m=read();
for(int i=1;i<=n;i++)
num[i]=read();
for(int i=1;i<=n;i++)
{
int Color=color[i]=read();
ans[Color][i%2]+=num[i];
len[Color][i%2]++;
ans[Color][i%2]%=Mod;
len[Color][i%2]%=Mod;
}
long long Ans=0;
for(int i=1;i<=n;i++)
Ans+=(ans[color[i]][i%2]+(len[color[i]][i%2]-2)*num[i]%Mod)%Mod*i,Ans%=Mod;
printf("%lld\n",Ans);
return 0;
}
The End
Thanks for reading!
-Lucky_Glass