[Noip2015PJ] 求和
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\)
- \(color_x=color_z\)
满足上述条件的三元组的分数规定为 \((x+z)\times (number_x+number_z)\)。整个纸带的分数规定为所有满足条件的三元组的分数的和。这个分数可能会很大,你只要输出整个纸带的分数除以 \(10,007\) 所得的余数即可。
Input
第一行是用一个空格隔开的两个正整数 \(n\) 和 \(m\) , \(n\) 表纸带上格子的个数, \(m\) 表纸带上颜色的种类数。
第二行有 \(n\) 用空格隔开的正整数,第 \(i\) 数字 \(number\) 表纸带上编号为 \(i\) 格子上面写的数字。
第三行有 \(n\) 用空格隔开的正整数,第 \(i\) 数字 \(color\) 表纸带上编号为 \(i\) 格子染的颜色。
Output
共一行,一个整数,表示所求的纸带分数除以 \(10,007\) 所得的余数。
Hint
对 于 全 部 \(10\) 组 数 据 , \(1\leq n\leq 100000, 1\leq m\leq 100000, 1\leq color_i\leq m,1\leq number_i\leq 100000\)
Solution
首先能够推导出 \(x\) 和 \(z\) 的编号一定是奇偶性相同的。
所以想到对于奇数点和偶数点分开统计答案。
拆一下 \((x,y,z)\) 三元组对答案产生的贡献:\((x+z)\times (number_x+number_z)=x\times number_x+x\times number_z+z\times number_x+z\times number_z\)
观察拆完的式子,发现后边三项中与 \(x\) 无关的项都能用前缀和维护。
比如说第二项中的 \(x\times number_z,number_z\) 显然能用前缀和统计。即扫描到 \(x\) 节点时它和后面某个点 \(z\) 对答案在第二项的贡献即为 \((qzh\_number[z]-qzh\_number[x])\times x\)。
所以得到了以下这个基本完成的算法:
对于每个颜色,分别开两个数组,存储奇数节点和偶数节点的编号,记为 \(odd\) 和 \(even\)。
对于每个颜色中的 \(even\) 和 \(odd\),再开三个前缀和数组分别维护 \(number_z,z,z\times number_z\) 的和。
然后扫描每个颜色,对每个颜色中存储的每个编号从头到尾扫一遍,答案加上这个点之后的所有点与这个点组成的三元组的贡献即可。
Code
#include<cstdio>
#include<vector>
#include<cctype>
#define N 100005
#define mod 10007
#define int long long
int ans;
int n,m;
int num[N];
int col[N];
bool vis[N];
std::vector<int> even[N],odd[N];
std::vector<int> qzh1[N],qzh2[N],qzh3[N];
std::vector<int> qzh4[N],qzh5[N],qzh6[N];
inline char nc(){
static const int BS=1<<22;
static unsigned char buf[BS],*st,*ed;
if(st==ed) ed=buf+fread(st=buf,1,BS,stdin);
return st==ed?EOF:*st++;
}
//#define nc getchar
inline int getint(){
char ch;
int res=0;
while(!isdigit(ch=nc()));
while(isdigit(ch)){
res=(res<<1)+(res<<3)+(ch^48);
ch=nc();
}
return res;
}
signed main(){
n=getint(),m=getint();
for(int i=1;i<=n;i++)
num[i]=getint();
for(int i=1;i<=n;i++){
col[i]=getint();
if(i&1){
odd[col[i]].push_back(i);
int a=qzh1[col[i]].size();
if(!qzh1[col[i]].empty())
qzh1[col[i]].push_back((qzh1[col[i]][a-1]+num[i]));
else qzh1[col[i]].push_back(num[i]);
a=qzh2[col[i]].size();
if(!qzh2[col[i]].empty())
qzh2[col[i]].push_back((qzh2[col[i]][a-1]+i));
else qzh2[col[i]].push_back(i);
a=qzh3[col[i]].size();
if(!qzh3[col[i]].empty())
qzh3[col[i]].push_back((qzh3[col[i]][a-1]+i*num[i]));
else qzh3[col[i]].push_back((i*num[i]));
}
else{
even[col[i]].push_back(i);
if(!qzh4[col[i]].empty())
qzh4[col[i]].push_back((qzh4[col[i]][qzh4[col[i]].size()-1]+num[i]));
else qzh4[col[i]].push_back(num[i]);
if(!qzh5[col[i]].empty())
qzh5[col[i]].push_back((qzh5[col[i]][qzh5[col[i]].size()-1]+i));
else qzh5[col[i]].push_back(i);
if(!qzh6[col[i]].empty())
qzh6[col[i]].push_back((qzh6[col[i]][qzh6[col[i]].size()-1]+i*num[i]));
else qzh6[col[i]].push_back((i*num[i]));
}
}
for(int k=1;k<=n;k++){
if(vis[col[k]]) continue;
vis[col[k]]=1;
for(int i=0;i<odd[col[k]].size();i++){
int p=odd[col[k]].size()-i-1;
(ans+=odd[col[k]][i]%mod*num[odd[col[k]][i]]%mod*p%mod)%=mod;
(ans+=odd[col[k]][i]%mod*(qzh1[col[k]][qzh1[col[k]].size()-1]-qzh1[col[k]][i])%mod)%=mod;
(ans+=num[odd[col[k]][i]]%mod*(qzh2[col[k]][qzh2[col[k]].size()-1]-qzh2[col[k]][i])%mod)%=mod;
(ans+=qzh3[col[k]][qzh3[col[k]].size()-1]-qzh3[col[k]][i])%=mod;
}
for(int i=0;i<even[col[k]].size();i++){
int p=even[col[k]].size()-i-1;
(ans+=even[col[k]][i]%mod*num[even[col[k]][i]]%mod*p%mod)%=mod;
(ans+=even[col[k]][i]%mod*(qzh4[col[k]][qzh4[col[k]].size()-1]-qzh4[col[k]][i])%mod)%=mod;
(ans+=num[even[col[k]][i]]%mod*(qzh5[col[k]][qzh5[col[k]].size()-1]-qzh5[col[k]][i])%mod)%=mod;
(ans+=qzh6[col[k]][qzh6[col[k]].size()-1]-qzh6[col[k]][i])%=mod;
}
}
printf("%lld\n",ans%mod);
return 0;
}