51nod 1712 区间求和

本文介绍了一种高效计算特定条件下区间和的方法,通过公式推导和维护关键数组来解决区间求和问题,实现了O(n)的时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接:点击打开链接


解法:

这个题首先考虑一个简单情况:对于区间[x,y],权值为多少。

容易写出公式:F[x , y] = S[y] - S[x-1] - (sum[y]-sum[x-1])*(x-1)+sum[x-1]*(y-x+1)

其中S[x]表示 从第一个元素到第x个元素的 所有有序二元组的和(题目中定义的),sum表示前缀和

整理一下就是:F[x , y] = S[y] - S[x-1] - sum[y]*(x-1) + sum(x-1)*y


这里要求的是所有a[x] == a[y]这样的区间和,注意到题目所有的数字都在10^6以内,所以可以开几个数组去维护每一个数字对应的信息。

继续对写出的式子进行讨论,假如对于当前给定的y,有多个x 满足a[x] == a[y]

答案就是

sigma F[x,y] = ct * S[y] - sigma S[x-1] - sum[y] * sigma (x-1) + sigma sum[x-1]* y

红色的部分就是需要维护的部分

开4个数组,对每个红字的部分,以数字为下标进行维护。

边扫描边维护。时间复杂度O(n)

注意mod


做这种题还是先写出公式,会思路清晰很多。


#include<iostream>
#include<string.h>
#include<string>
#include<algorithm>
#include<stdio.h>
#define LL long long int
using namespace std;
const int maxn = 1100000;
LL mod = ( (1LL)<<32);

LL ct[maxn];
LL sx[maxn];
LL lx[maxn];
LL ss[maxn];
int n;
void get(int &x)
{
    char c = getchar(); x = 0;
    while(c < '0' || c > '9') c = getchar();
    while(c <= '9' && c >= '0') x = x*10+c-48, c = getchar();
}

void init()
{
    memset(ct,0,sizeof(ct));
    memset(sx,0,sizeof(sx));
    memset(lx,0,sizeof(lx));
    memset(ss,0,sizeof(ss));
}


int main()
{
    while(cin>>n)
    {
        init();
        LL sum = 0;
        LL ts = 0;
        LL ans = 0;
        for(int i = 1;i<=n;i++)
        {
            int in ;
            get(in);

            LL tmp = (LL)in;

            LL save = sum;
            LL save2 = ts;
            sum = (sum + (((i-1)*tmp)%mod - ts + mod)% mod)%mod;
            ts += tmp;
            ts %= mod;

            LL x = ct[tmp]*sum%mod;
            x = (x - sx[tmp] + mod) % mod;
            x = (x - (lx[tmp]*ts%mod) + mod)%mod;
            x = (x + i*ss[tmp]%mod) % mod;
            ans = ans + x;
            ans %= mod;


            ct[tmp] ++;
            sx[tmp] += save;
            sx[tmp] %= mod;
            lx[tmp] += (i-1);
            lx[tmp] %= mod;
            ss[tmp] += save2;
            ss[tmp] %= mod;
        }
        printf("%lld\n",ans);

    }
    return 0;
}






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值