题目描述
一条狭长的纸带被均匀划分出了nn个格子,格子编号从11到nn。每个格子上都染了一种颜色color\_icolor_i用[1,m][1,m]当中的一个整数表示),并且写了一个数字number\_inumber_i。
定义一种特殊的三元组:(x,y,z)(x,y,z),其中x,y,zx,y,z都代表纸带上格子的编号,这里的三元组要求满足以下两个条件:
-
x,y,z是整数,x<y<z,y-x=z-y
-
colorx=colorz
满足上述条件的三元组的分数规定为)(x+z)×(number_x+number_z)。整个纸带的分数规定为所有满足条件的三元组的分数的和。这个分数可能会很大,你只要输出整个纸带的分数除以10,007所得的余数即可。
输入格式:
第一行是用一个空格隔开的两个正整数nn和m,nm,n表纸带上格子的个数,mm表纸带上颜色的种类数。
第二行有nn用空格隔开的正整数,第ii数字numbernumber表纸带上编号为ii格子上面写的数字。
第三行有nn用空格隔开的正整数,第ii数字colorcolor表纸带上编号为ii格子染的颜色。
输出格式:
一个整数,表示所求的纸带分数除以1000710007所得的余数。
思路分析
直接暴力肯定是不行的,我们需要把各种相同的颜色分离出来计算。
但是只分离出来没有用,因为三元组要求中间有一个y。可以看出,x和z的奇偶性相同时,存在y使三元组成立。
所以我们用两个数组存储相同颜色的奇、偶情况,每次计算就变成计算这个序列的值。
则原式=(num[1]+num[2])*(a[num[1]]+a[num[2])+(num[1]+num[3])*(a[num[1]]+a[num[3])+(num[1]+num[4])*(a[num[1]]+a[num[4])+
...+(num[2]+num[3])*(a[num[2]]+a[num[3])+(num[2]+num[4])*(a[num[2]]+a[num[4])+
...+(num[n-1]+num[n])*(a[num[n-1]]+a[num[n])
其中,num[1]*a[num[1]]这个值被计算了n-1次,num[1]乘其他a[num[2]],a[num[3]],a[num[4]]……都为一次。
num[2]*a[num[2]]这个值被计算了n-2次,num[1]乘其他a[num[1]],a[num[3]],a[num[4]]……都为一次。
以此类推。
代码
#include<iostream> #include<vector> #include<cmath> using namespace std; long long n,m,a[100001],ans=0; //数据比较大,用longlong vector<long long> even[100001],odd[100001]; //使用变长数组存储 int ad(int pt) { long long count=0,ep=even[pt].size(),op=odd[pt].size(),s=0; for(int i=1;i<=ep;i++) { s=(s+a[even[pt][i-1]])%10007;//所有值的总和 } for(int i=1;i<=ep;i++) { count+=(s%10007+(ep-2)*(a[even[pt][i-1]]%10007))%10007*even[pt][i-1]%10007; count%=10007; //一定要边/边%! } s=0; for(int i=1;i<=op;i++) { s=(s+a[odd[pt][i-1]])%10007; } for(int i=1;i<=op;i++) { count+=(s%10007+(op-2)*(a[odd[pt][i-1]]%10007))%10007*odd[pt][i-1]%10007; count%=10007; } return count; } int main() { long long cl; cin>>n>>m; for(int i=1;i<=n;i++) { cin>>a[i]; } for(int i=1;i<=n;i++) { cin>>cl; if(i&1) odd[cl].push_back(i); else even[cl].push_back(i); } for(int i=1;i<=m;i++) { ans+=ad(i); ans%=10007; } cout<<ans; }