区间求和(莫队算法)

链接:https://ac.nowcoder.com/acm/contest/1085/G
来源:牛客网

小sun最近突然对区间来了兴趣,现在他有这样一个问题想问问你:
给你n个数,每个数为ai,现在有m个询问,每个询问l,r,需要求出:

 

 链接:https://ac.nowcoder.com/acm/contest/1085/G
来源:牛客网

输入描述:

第一行,两个整数n,m

第二行,总共n个数,代表这个数列

接下来m行,每行两个整数l,r,代表一个询问

输出描述:

输出总共m行,对于每个询问,输出这个询问对应的答案
示例1

输入

复制
10 5
1 3 2 4 5 6 4 5 6 7
1 5
2 5
3 4
1 10
3 7

输出

复制
15
14
6
73
29

备注:

1n,m1e5
1ai1e5
1≤l,r≤n
经典莫队算法:维护区间某个数的出现次数
但是这个让求的是区间内的ai*num[i]
如果区间内有两个5就是加上5*2+5*2
不是简单的加a[i]
#include<iostream>
#include<algorithm>
using namespace std;
const int maxn=5e5+100;
typedef long long ll; 
int n,m;
int a[maxn];
ll ans[maxn];
int vis[maxn]; 
struct node{
    int l,r;
    int id;
}t[maxn];
int mp[maxn],block,belong[maxn];
bool cmp(node x,node y){
    if(belong[x.l]!=belong[y.l]){
        return belong[x.l]<belong[y.l];
    }
    else{
        if(belong[x.l]&1){
            return x.r<y.r;
        }
        else{
            return x.r>y.r;
        }
    }
}
ll res;
void remove(int pos){
    res-=1ll*a[pos]*vis[a[pos]]*vis[a[pos]];//先把之前的权值减去在加上之后的权值 
    vis[a[pos]]--; 
    res+=1ll*a[pos]*vis[a[pos]]*vis[a[pos]];
}
void add(int pos){
    res-=1ll*a[pos]*vis[a[pos]]*vis[a[pos]]; 
    vis[a[pos]]++; 
    res+=1ll*a[pos]*vis[a[pos]]*vis[a[pos]];
}
int main(){
    scanf("%d%d",&n,&m);
    block=sqrt(n);
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        belong[i]=(i-1)/block+1;
        
    }//分块处理
    for(int i=1;i<=m;i++){
        scanf("%d%d",&t[i].l,&t[i].r);
        t[i].id=i;
    } 
    sort(t+1,t+m+1,cmp);
    int l=1,r=0;
    int ql,qr; 
    for(int i=1;i<=m;i++){
        ql=t[i].l,qr=t[i].r;
        while(l<ql){
            remove(l++);
        } 
        while(l>ql){
            add(--l);
        }
        while(r>qr){
            remove(r--);
        }
        while(r<qr){
            add(++r);
        }
        ans[t[i].id]=res; 
    }
    for(int i=1;i<=m;i++){
        printf("%lld\n",ans[i]);
    }
    
}

 

 

 

posted @ 2021-01-18 21:37  lipu123  阅读(435)  评论(0编辑  收藏  举报