小游戏

有位老铁设计了一个跳格子游戏,游戏有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);
    
    
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值