zoukankan      html  css  js  c++  java
  • NBUT 1457 Sona (莫队算法)

    • [1457] Sona

    • 时间限制: 5000 ms 内存限制: 65535 K
    • 问题描述
    • Sona, Maven of the Strings. Of cause, she can play the zither.

       

      Sona can't speak but she can make fancy music. Her music can attack, heal, encourage and enchant.

       

      There're an ancient score(乐谱). But because it's too long, Sona can't play it in a short moment. So Sona decide to just play a part of it and revise it.

       

      A score is composed of notes. There are 109 kinds of notes and a score has105 notes at most.

       

      To diversify Sona's own score, she have to select several parts of it. The energy of each part is calculated like that:

       

      Count the number of times that each notes appear. Sum each of the number of times' cube together. And the sum is the energy.

       

      You should help Sona to calculate out the energy of each part.

    • 输入
    • This problem contains several cases. And this problem provides 2 seconds to run.
      The first line of each case is an integer N (1 ≤ N ≤ 10^5), indicates the number of notes.
      Then N numbers followed. Each number is a kind of note. (1 ≤ NOTE ≤ 10^9)
      Next line is an integer Q (1 ≤ Q ≤ 10^5), indicates the number of parts.
      Next Q parts followed. Each part contains 2 integers Li and Ri, indicates the left side of the part and the right side of the part.
    • 输出
    • For each part, you should output the energy of that part.
    • 样例输入
    • 8
      1 1 3 1 3 1 3 3
      4
      1 8
      3 8
      5 6
      5 5
      
    • 样例输出
    • 128
      72
      2
      1
      
    • 提示
    • 来源
    • XadillaX

    题意:有N个数,有M个询问,求每次询问的区间[L,R]中,每种数字出现次数的立方和

    分析:一开始不会莫队算法,结果想了很久,分析复杂度之后发现在转移区间这一块复杂度很大。

    后来发现莫队算法能很好解决这个问题。

    那么就先说一下莫队算法吧,其实这个算法还挺简单的,能够离线处理一类区间查询不修改类问题。如果知道[L,R]的答案,那么也可通过计算得到[L-1,R],[L-1,R+1],[L,R-1],[L,R+1]四个区间的答案,复杂度是O(1)。

    莫队算法本质是在暴力上进行了改良,离线保存所有的查询,然后以一定的顺序进行排序,然后通过区间的变化来得到答案,减少了区间移动幅度过大带来的时间复杂度。至于用什么排序,一般采用的排序方法就是把长度为N的区间分为block=sqrt(n)份,然后把所有区间[L,R]以 L/block 为第一基准,以R为第二基准进行二级排序。然后要注意区间是先扩大还是先缩小,不同的题目处理不同。

    这道题的话,是要计算一个区间内每种数出现次数的立方和,那么转移的话,假设一个区间是[L,R],如果向左转移,用ans保存区间的答案,cnt[i]记录第 i 种数出现的次数,那么假设向左转移新加进区间的数字是a,由于a新加进区间,cnt[a]++,ans=ans-(cnt[a]-1)^3+(cnt[a])^3。数字移除区间反之。

    #pragma comprint(linker, "/STACK:1024000000,1024000000")
    #include<cstdio>
    #include<string>
    #include<iostream>
    #include<cstring>
    #include<cmath>
    #include<stack>
    #include<queue>
    #include<vector>
    #include<map>
    #include<stdlib.h>
    #include<time.h>
    #include<algorithm>
    #define LL __int64
    #define FIN freopen("in.txt","r",stdin)
    using namespace std;
    const int MAXN=100000+5;
    int block;
    struct node
    {
        int l,r,id;
        int b;
        void init()
        {
            b=l/block;
        }
        bool operator<(const node A)const
        {
            if(b==A.b) return r<A.r;
            return b<A.b;
        }
    }po[MAXN];
    
    int n,Q;
    int a[MAXN],b[MAXN];
    LL cnt[MAXN],ans[MAXN],lastans;
    
    LL calc(LL x)
    {
        return x*x*x;
    }
    void update(int pos,int x)
    {
        lastans-=calc(cnt[a[pos]]);
        lastans+=calc(cnt[a[pos]]+=x);
    }
    int main()
    {
        //FIN;
        while(scanf("%d",&n)!=EOF)
        {
            memset(a,0,sizeof(a));
            for(int i=1;i<=n;i++)
            {
                scanf("%d",&a[i]);
                b[i]=a[i];
            }
            sort(b+1,b+n+1);
            int sz=unique(b+1,b+n+1)-b;
            for(int i=1;i<=n;i++)
            {
                a[i]=lower_bound(b+1,b+sz+1,a[i])-b;
            }                                           //离散化
            scanf("%d",&Q);
            block=sqrt(n+0.5);                          //分块
            for(int i=0;i<Q;i++)
            {
                int x,y;
                scanf("%d %d",&x,&y);
                po[i].l=x;
                po[i].r=y;
                po[i].id=i;
                po[i].init();
            }
            sort(po,po+Q);
            int lastl=2,lastr=1;
            lastans=0;
            memset(cnt,0,sizeof(cnt));
            for(int i=0;i<Q;i++)
            {
                while(lastl>po[i].l) update(--lastl,1);   //更行区间,本题要先扩大区间,不然会出现负值,虽然不一定会错,但是先扩大区间显然更优
                while(lastr<po[i].r) update(++lastr,1);
                while(lastl<po[i].l) update(lastl++,-1);
                while(lastr>po[i].r) update(lastr--,-1);
                ans[po[i].id]=lastans;
            }
            for (int i=0;i<Q;i++)
                printf("%I64d
    ",ans[i]);
        }
        return 0;
    }
    View Code
  • 相关阅读:
    基于properties文件的Spring Boot多环境切换
    在mysql命令行下执行sql文件
    thymeleaf th:href 多个参数传递格式
    Mybatis 级联查询时只查出了一条数据
    IDEA技巧-自动导包(Auto import)以及自动优化包
    Drools学习教程
    Drools学习教程
    Drools学习教程
    Drools学习教程
    一道有趣的类加载面试题
  • 原文地址:https://www.cnblogs.com/clliff/p/4750594.html
Copyright © 2011-2022 走看看