求和-前缀和

题目描述

一条狭长的纸带被均匀划分出了 n 个格子,格子编号从 1 到 n。每个格子上都染了一种颜色 𝑐𝑜𝑙𝑜𝑟𝑖(用[1,m]当中的一个整数表示),并且写了一个数字 𝑛𝑢𝑏𝑒𝑟𝑖。

定义一种特殊的三元组:(x,y,z),其中 x,y,z 都代表纸带上格子的编号,这里的三元组要求满足以下两个条件:

  1. 𝑥,𝑦,𝑧 都是整数, 𝑥<𝑦<𝑧,𝑦−𝑥=𝑧−𝑦;

  2. 𝑐𝑜𝑙𝑜𝑟𝑥=𝑐𝑜𝑙𝑜𝑟𝑧。

满足上述条件的三元组的分数规定为 (x+z)∗(𝑛𝑢𝑚𝑏𝑒𝑟𝑥+𝑛𝑢𝑚𝑏𝑒𝑟𝑧)。整个纸带的分数规定为所有满足条件的三元组的分数的和。这个分数可能会很大,你只要输出整个纸带的分数除以 10007 所得的余数即可。

输入描述

第一行是用一个空格隔开的两个正整数 𝑛,𝑚,𝑛 代表纸带上格子的个数,𝑚 代表纸带上颜色的种类数。

第二行有 𝑛 个用空格隔开的正整数,第 𝑖 个数字 𝑛𝑢𝑚𝑏𝑒𝑟𝑖 代表纸带上编号为 𝑖 的格子上面写的数字。

第三行有 𝑛 个用空格隔开的正整数,第 𝑖 个数字 𝑐𝑜𝑙𝑜𝑟𝑖 代表纸带上编号为 𝑖 的格子染的颜色。

其中, 1≤𝑛≤105,1≤𝑚≤105,1≤𝑐𝑜𝑙𝑜𝑟𝑖≤𝑚,1≤𝑛𝑢𝑚𝑏𝑒𝑟𝑖≤105。

输出描述

输出共一行,一个整数,表示所求的纸带分数除以 10007 所得的余数。

示例 1

输入

6 2
5 5 3 2 2 2
2 2 1 1 2 1

输出

82

样例说明

纸带如题干中的图所示。

所有满足条件的三元组为:(1, 3, 5), (4, 5, 6)。

所以纸带的分数为(1 + 5) ∗ (5 + 2) + (4 + 6) ∗ (2 + 2) = 42 + 40 = 82。

示例 2

输入

15 4
5 10 8 2 2 2 9 9 7 7 5 6 4 2 4
2 2 3 3 4 3 3 2 4 4 4 4 1 1 1

输出

1388

运行限制 

  • 最大运行时间:1s
  • 最大运行内存: 256M

 解析:

 暴力求解法:

  时间复杂度过高,过不了。

#include <iostream>
#include<vector>
using namespace std;
typedef long long ll;
const ll MAX = 1e5+4;
const ll M = 1e4 + 7;
ll sum = 0;
ll numberi[MAX];
ll colori[MAX];
int main() 
{
    ll n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
    {
        cin >> numberi[i];
    }
    for (int i = 1; i <= n; i++)
    {
        cin >> colori[i];
    }
    //通过y−x=z−y  2y=x+z  奇数一组  偶数一组 符合要求
    for (int i = 1; i <= n; i += 2)//奇数组
    {
        for (int j = i + 2; j <= n; j += 2)
        {
            if (colori[i] == colori[j])
            {
                sum = (sum + (i + j) * (numberi[i] + numberi[j]) % M) % M;
            }
        }
    }
    for (int i = 2; i <= n; i += 2)//偶数组
    {
        for (int j = i + 2; j <= n; j += 2)
        {
            if (colori[i] == colori[j])
            {
                sum = (sum + (i + j) * (numberi[i] + numberi[j]) % M) % M;
            }
        }
    }
    cout << sum << endl;
    return 0;
}
公式转化法:

 

#include <iostream>
#include<vector>
#include<map>
using namespace std;
typedef long long ll;
const ll MAX = 1e5+4;
const ll M = 1e4 + 7;
ll sum = 0;
ll numberi[MAX];
ll colori[MAX];
map<ll, vector<ll>>mp;//颜色编号  格子编号
int main() 
{
    ll n, m;
    cin >> n >> m;
    for (int i = 1; i <= n; i++)
    {
        cin >> numberi[i];
    }
    for (int i = 1; i <= n; i++)
    {
        cin >> colori[i];
        mp[colori[i]].push_back(i);//通过颜色分开 相同颜色的一组
    }
    for (int i = 1; i <= m; i++)//颜色的总数
    {
        ll i1 = 0, i2 = 0;
        ll ax1 = 0, ax2 = 0;
        ll a_sum1 = 0, a_sum2 = 0;
        ll x_sum1 = 0, x_sum2 = 0;
        if (mp[i].size())//该颜色下不为空
        {
            for (auto j = mp[i].begin(); j != mp[i].end(); j++)
            {
                if (*j % 2)//奇数一组
                {
                    i1++;
                    ax1 = (ax1 + *j * numberi[*j] % M) % M;
                    a_sum1 =(a_sum1+ *j)%M;
                    x_sum1 = (x_sum1 + numberi[*j])%M;
                }
                else//偶数一组
                {
                    i2++;
                    ax2 = (ax2 + *j * numberi[*j] % M) % M;
                    a_sum2 = (a_sum2 + *j) % M;
                    x_sum2 = (x_sum2 + numberi[*j]) % M;
                }
            }
            if (i1 >= 2)
            {
                sum = (sum + (ax1 * (i1 - 2)) % M + a_sum1 * x_sum1 % M) % M;
            }
            if (i2 >= 2)
            {
                sum = (sum + (ax2 * (i2 - 2)) % M + a_sum2 * x_sum2 % M) % M;
            }
        }
    }
    cout << sum << endl;
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值