有位老铁设计了一个跳格子游戏,游戏有N个格子顺序排成一行,编号从1到N,每个格子有点数Qi,有标记Li(标记的范围是1-M),每次跳格子,要选择一个格子a,以任意正偶数距离x跳到格子b,如果格子b在游戏区域内,且La=Lb,则称为一次合法跳跃,获得的分数是(a + b) * (Qa + Qb)。
在继续设计游戏玩法时,这位老铁纠结了很久,于是他决定放弃……但是他想知道所有合法跳跃总共能获得多少分。
输入描述:
第一行N,M,表示格子数和标记种类数, 第二行N个数,表示格子的点数 第三行N个数,表示每个格子的标记
输出描述:
一个整数P,表示总共能获得的分数,由于分数可能很大,这里只需要输出分数除以10007的余数
输入例子1:
5 2 1 2 3 4 5 1 2 1 2 1
输出例子1:
152
例子说明1:
样例输出 样例解析,从1号格子可以调到3,5,2号格子跳到4,3号格子跳到5,所以分数为 (1+3)*(1+3) + (1 + 5)*(1+5) + (3+5)*(3+5) + (2+4)*(2+4) = 152
这道题有点难,坑。
这道题首先就是找出相同标记的、距离是偶数的,然后对每对位置进行(位置1+位置2)*(点数1+点数2),求和。
1.乘法溢出,位置和点数相乘的时候溢出了,需要转换为long long
2.奇偶位置分开,一开始用二重for循环,通过if判断奇偶,但是超时了,就想着把长度减少。多了10%case通过。
3.拆开乘法的两个因式,对每对位置进行求乘法,乘法里面分别包括两个位置和两个点数,这是一种对称结构,在这我们考虑位置(考虑点数也是一样的)作为主体(就是公因数),每一个位置都和其他位置进行求乘法,对于每一个位置,有:
某位置((n-1)*某位置的点数 + 其他位置的点数和),其他位置的点数和的个数是n-1个。
为了减少运算量,对每个位置找出共同部分,把一个某位置的点数合并到其他位置的点数和,变成总和,得到:
某位置((n-2)*某位置的点数 + 所有位置的点数和)
所有位置的点数和可以求一遍,所以复杂度从n^2到n。
#include<stdio.h>
#include<vector>
using namespace std;
int n,m;
vector<int> dianshu;
vector<int> biaoji;
vector<vector<int> > biaoji_pos_jishu;
vector<vector<int> > biaoji_pos_oushu;
int cal(const vector<vector<int> > &pos){
long long ans=0;
for(int i=1;i<=m;i++){
long long sum=0;
for(int j=0;j<pos[i].size();j++){
sum=sum+dianshu[pos[i][j]];
sum%=10007;
}
for(int j=0;j<pos[i].size();j++){
ans= ans + ( pos[i][j]+1) * ( ((long long)(pos[i].size()-2) * dianshu[pos[i][j]])%10007 + sum);
ans=ans%10007;
}
}
return ans;
}
int main(){
scanf("%d%d",&n,&m);
dianshu.resize(n);
biaoji.resize(n);
biaoji_pos_jishu.resize(m+1);
biaoji_pos_oushu.resize(m+1);
for(int i=0;i<n;i++){
int num;
scanf("%d",&num);
dianshu[i]=num;
}
for(int i=0;i<n;i++){
int num;
scanf("%d",&num);
biaoji[i]=num;
}
for(int i=0;i<n;i++){
if(i%2==0){
biaoji_pos_oushu[biaoji[i]].push_back(i);
}
else{
biaoji_pos_jishu[biaoji[i]].push_back(i);
}
}
long long ans=0;
ans+=cal(biaoji_pos_jishu);
ans+=cal(biaoji_pos_oushu);
ans=ans%10007;
printf("%lld\n",ans);
}
3155

被折叠的 条评论
为什么被折叠?



