Powerful array – CF Div1 D(莫队算法)

题目链接:https://vjudge.net/problem/CodeForces-86D

题意:给你一连串的数,有很多的询问区间,记一个区间的一个数(x)的个数为num,求num*num*x。

思路:这种涉及很多区间中数字个数统计的查询,并且不涉及区间的修改操作时,可以直接用莫队算法

莫队的核心就是充分利用上一次的查询结果(其实就是通过上一次查询后的cnt数组的变化,充分利用上一次询问的结果,得到本次询问的结果),由于这里是平方,所以需要稍稍转换一下,当cnt从n变为n+1,它的平方实际上就是增加了(2*n+1),【(n+1)^2-n^2=2*n+1】,同理,当cnt从n变为n-1,就是减少了2*n-1,那么每一次的区间询问结果与上一次询问的结果,就相差(cnt的变化量*这个数字的值),这样只要相应的变化上次询问的结果就行。

写题过程:一开始没用读入挂,交了一发,在test6超时了,然后用了读入挂,还是超时,最后才发现,是重载'<'时,我用的if...else...表达式,改成条件运算符就过了,不明所以。。。在看别人的代码时,发现可以不用专门写一个分块函数以及分块数组,直接用一个unit就行,以后可以采用这种方法,尽管这个比我用分块函数以及分块数组的代码慢了46Ms,不过可以接受啦,毕竟代码比较简洁。

这些都在下面的代码中有注释,可以参看。

代码:

#include <map>
#include <set>
#include <stack>
#include <cmath>
#include <queue>
#include <vector>
#include <cstdio>
#include <string>
#include <cstring>
#include <sstream>
#include <iostream>
#include <algorithm>
#define Fin             freopen("in.txt","r",stdin)
#define Fout            freopen("out.txt","w",stdout)
#define Case(T)         int T;for(scanf("%d",&T);T--;)
#define fo(i,a,b)              for(int i = a; i < b; ++i)
#define fd(i,a,b)              for(int i = a; i >= b; --i)
#define me(a,b) memset(a,b,sizeof(a))
#define fi(a,n,val)    fill(a,a+n,val)
#define Scand(n)       scanf("%d",&n)
#define Scand2(a,b)     scanf("%d%d",&a,&b)
#define Scand3(a,b,c)     scanf("%d%d%d",&a,&b,&c)
#define Scand4(a,b,c,d)     scanf("%d%d%d%d",&a,&b,&c,&d)
#define Scans(s)       scanf("%s",s)
using namespace std;
typedef long long ll;
ll gcd(ll a,ll b) { return b ? gcd(b,a%b): a; }
const int maxn = 200000 + 50;
const int INF = 0xffffff;

#ifndef ONLINE_JUDGE

#endif // ONLINE_JUDGE

inline int read(){
    int sgn = 1; int sum = 0;
    char ch = getchar();
    while (ch < '0' || ch > '9') {
        if(ch == '-')   sgn = -sgn;
        ch = getchar();
    }
    while ('0' <= ch && ch <= '9') {
        sum = sum*10+(ch-'0');
        ch = getchar();
    }
    return sgn*sum;
}

int n,t;
int arr[maxn];
int cnt[10000005], id[maxn];
ll ans[maxn];
int L,R;
int unit;
ll now;

struct range{
    int l,r;
    int askorder;
    friend bool operator < (const range &a, const range &b){
//         if(id[a.l] == id[a.l])           //①这种if...else...写法会超时
//             return a.r < b.r;
//        return id[a.l] < id[b.l];
//        return id[a.l] == id[b.l] ? a.r < b.r : id[a.l] < id[b.l];        //②这个得开一个id数组,但是很快
        return a.l/unit != b.l/unit ? a.l/unit < b.l/unit : a.r < b.r;                //③这个不用开数组,只要求出unit的值就行,比②略慢一点点
    }
}rangee[maxn];

void blocker(){
    int k = sqrt(n);
    fo(i, 1, n+1)
        id[i] = (i-1)/k+1;
}

void move(int x, int type){
    if(type == 0){
        now = now-((cnt[arr[x]]<<1)-1)*(ll)arr[x];
        cnt[arr[x]]--;
    }else if(type == 1){
        now = now+((cnt[arr[x]]<<1)|1)*(ll)arr[x];
        cnt[arr[x]]++;
    }
}

int main()
{
#ifndef ONLINE_JUDGE
    //Fin;
#endif // ONLINE_JUDGE
    n = read(); t = read();
    fo(i, 1, n+1)
        arr[i] = read();
//    blocker();
    unit = sqrt(n);
    for(int i = 1; i <= t; ++i){
        rangee[i].l = read();
        rangee[i].r = read();
        rangee[i].askorder = i;
    }
    sort(rangee+1, rangee+t+1);
    L = 0; R = 0; now = 0;
    for(int i = 1; i <= t; ++i){
        while (L < rangee[i].l) {
            move(L++,0);
        }
        while (R > rangee[i].r) {
            move(R--,0);
        }
        
        while (L > rangee[i].l) {
            move(--L,1);
        }
        while (R < rangee[i].r) {
            move(++R,1);
        }
        ans[rangee[i].askorder] = now;
    }
    for(int i = 1;i <= t; ++i)
    {
        printf("%lld\n",ans[i]);
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值