通过暴力枚举每一个三元组时间复杂度O(n2),期望得分60.
n<=100000,要考虑怎么把时间复杂度降下来.
计num[x]为N(x).三元组的分数计算公式为(x+z)(N(x)+N(z))
可以发现,一个三元组的得分与中间项无关,另两项应满足颜色相同且同奇偶(即满足(x+z)mod2==0),所以把同颜色,同奇偶的项放到一起,这样在循环计算时就不必要判断得分的合法性.
可是这样做还是不能减少循环,计算过程中,一个纸片的得分被计算了多次,能不能把前面算过的合并成一个呢?
对于两个同色同奇偶的纸片x,y, y与x的得分为
(x+y)∗(N(x)+N(y))=x∗N(x)+y∗N(y)+x∗N(y)+y∗N(x)
对于三个同色同奇偶的纸片x,y,z, z与x,y的得分为
(x+z)∗(N(x)+N(z))+(y+z)∗(N(y)+N(z))
=x∗N(x)+y∗N(y)+2∗z∗N(z)+(x+y)∗N(z)+(N(x)+N(y))∗z
可以发现,对于已经计算完的纸片,可以统计编号和、num值之和 与 编号与num值乘积之和,每次计算将他们加到答案中,可以省掉一层循环.时间复杂度降为O(n)
#include <cstdio>
#include <iostream>
#include <vector>
#define N 100005
using namespace std;
const int mod=10007;
long long num[N],top1[N],top2[N],d[N];
vector<int> a[N];
vector<int> b[N];
long long ans;
int main(){
//freopen("sum.in","r",stdin); freopen("sum.out","w",stdout);
int n,m;
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%lld",&num[i]),d[i]=i*num[i];
for(int i=1;i<=n;i++) {
int x;
scanf("%d",&x);
if(i&1) a[x].push_back(i),top1[x]++;
else b[x].push_back(i),top2[x]++;
}
for(int i=1;i<=m;i++){
long long x,y,z;
if(top1[i]>0) x=d[a[i][0]],y=a[i][0],z=num[y];
for(int t=1;t<top1[i];t++){
int j=a[i][t];
ans=(ans+x+(long long)y*num[j]+z*j+t*d[j])%mod;
x=(x+d[j])%mod; y=(y+j)%mod; z=(z+num[j])%mod;
}
if(top2[i]>0) x=d[b[i][0]],y=b[i][0],z=num[y];
for(int t=1;t<top2[i];t++){
long long j=b[i][t];
ans=(ans+x+(long long)y*num[j]+z*j+t*d[j])%mod;
x=(x+d[j])%mod; y=(y+j)%mod; z=(z+num[j])%mod;
}
}
cout<<ans;
return 0;
}