洛谷P2671 [NOIP2015 普及组] 求和

题目链接:

[NOIP2015 普及组] 求和 - 洛谷

思路:

洛谷将这道题放到差分前缀和专题中,但我觉得这个知识点在这道题中并没有很大的体现。

核心:分组!

分组条件: 

根据:奇数+奇数=偶数 ,偶数+偶数=偶数,所以符合条件的x和z一定都是奇数或者都是偶数。再根据颜色分组。下面代码用a[c][j]表示分组的结果,其中c表示颜色种类,j表示奇偶情况。

所以最终只有一个组内的元素,两两之间符合限定条件,那么现在问题便转化为如何计算一个组内所有元素两两匹配,构成的(x+z)*(Numx+Numz)。

这里先假设一个组内有3个元素x1,x2,x3,此时再插入一个元素z,ans会增加多少?

计算得,增加:

(x1+z)(Numx1+Numz)+(x2+z)(Numx2+Numz)+(x3+z)(Numx3+Numz)

=(x1*Numx1 + x2*Numx2 + x3*Numx3) + 3z*Numz + (x1+x2+x3)*Numz + z*(Numx1+Numx2+Numx3)​​​​​​​

 观察可得,只需记录前3项的四个值便可快速计算出新增1个元素ans增加的值。

四个值分别为:

\sum_{i=1}^{cnt} X_i                        \sum_{i=1}^{cnt} Num(X_i)                        \sum_{i=1}^{cnt} X_i *Num(X_i)  

以及这个组中元素的个数cnt      

易得,新增一个元素z,ans增加的值为

\sum_{i=1}^{cnt} X_i *Num(X_i) + Num(z)* \sum_{i=1}^{cnt}X_i + z*\sum_{i=1}^{cnt} Num(X_i)+cnt*z*Num(z)

问题解决,编写代码:

#include<iostream>
using namespace std;

typedef long long ll;
const int MAXN = 100005;
const int MOD = 10007;

struct cj
{
    ll sum_xi;
    ll sum_num_xi;
    ll sum_xi_num_xi;
    ll cnt;
};

cj a[MAXN][2];
ll ans=0;
ll color[MAXN],num[MAXN];
ll n,m;
ll c,j;

int main()
{
    cin>>n>>m;

    for(int i=1;i<=n;++i) cin>>num[i];
    for(int i=1;i<=n;++i) cin>>color[i];

    for(int i=1;i<=n;++i)
    {
        c=color[i];
        j=i%2;

        ans+=a[c][j].sum_xi_num_xi+a[c][j].cnt*i*num[i]+a[c][j].sum_xi*num[i]+i*a[c][j].sum_num_xi;
        ans%=MOD;
        a[c][j].cnt++;
        a[c][j].sum_xi=(i+a[c][j].sum_xi)%MOD;
        a[c][j].sum_num_xi=(num[i]+a[c][j].sum_num_xi)%MOD;
        a[c][j].sum_xi_num_xi=(a[c][j].sum_xi_num_xi+i*num[i])%MOD;
    }

    cout<<ans<<endl;

    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值