zoukankan      html  css  js  c++  java
  • P2709-小B的询问-(莫队算法)

    莫队算法被称为优雅的暴力,是一种 毒瘤暴力的 区间操作算法。初学莫队,记录一下思想。

    对于多个区间查询[l,r]这类问题,可以离线操作,常规做法还有对左区间或者右区间从小到大排序,让左指针或者右指针只走一遍,有效降低时间复杂度。但是遇到[1,1000000],[2,3],[3,1000000],[4,5],[5,1000000]这样的查询区间,虽然可以保证左区间只扫一遍,但是右指针需要从头到尾来回扫,时间复杂度一下子就上去了。莫队算法就是专门针对这种毒瘤题目的。

    它的主要思想是分块,对区间的范围n开根号得到一个d,每一个查询区间[l,r]的左区间l除以d,区间看作是全局的第x块,不同块按的区间块从小到大排序,同一块的按右区间从小到大排序(不同块看左,同块看右)。分块排序写一个自定义比较函数就可以了。时间复杂度n*sqrt(n),具体证明看其他大佬博客吧。

    对于查询的区间[l,r],指针移动操作比较常规,无非是左指针和右指针左右移动,[5,7]的答案可以由[5,8]或者[5,6]移动一次右指针解决,也可以由[4,7]或[6,7]移动一次左指针解决。对于区间查询的贡献,减去原来的,加上现在的。

    入门题P2709

    题意看了很久才懂,解释一下数据,1到4出现了2个1,对答案贡献2*2,出现1个2,对答案贡献1*1,出现1个3,对答案贡献1*1,加起来就是6。

    #include<stdio.h>
    #include<iostream>
    #include<algorithm>
    #include<cstring>
    #include<math.h>
    #include<string>
    #include<map>
    #include<queue>
    #include<stack>
    #include<set>
    #include<ctime>
    #define ll long long
    #define inf 0x3f3f3f3f ///填充无限小-0x7f
    using namespace std;
    
    struct node
    {
        int l;
        int d;
        int r;
        int idx;
        ll ans;
    };
    node b[50005];
    int a[50005];
    int num[50005];
    int n,m,k;
    int d;
    
    
    bool cmp1(node p1,node p2)///分块,对左区间开根号,左边不在同一块按左区间小到大排序,左边在同一块按右边大小排序
    {
        if(p1.d<p2.d)
            return true;
        else if(p1.d==p2.d)
            return p1.r<p2.r;
        else
            return false;
    }
    
    bool cmp2(node p1,node p2)///离线操作后恢复答案次序
    {
        return p1.idx<p2.idx;
    }
    
    
    int main()///P2709 莫队入门
    {
        scanf("%d%d%d",&n,&m,&k);
        d=sqrt(n);///开根号的分块标准
        for(int i=1;i<=n;i++)
            scanf("%d",&a[i]);
        for(int i=1;i<=m;i++)
        {
            int l,r;
            scanf("%d%d",&l,&r);
            b[i].l=l;
            b[i].d=l/d;///分块
            b[i].r=r;
            b[i].idx=i;
        }
    
        sort(b+1,b+m+1,cmp1);
        int L=1,R=1;
        num[ a[1] ]=1;
        ll ans=1;
        for(int i=1;i<=m;i++)
        {
            while(L>b[i].l)///左指针往左
            {
                L--;
                ans=ans-num[ a[L] ]*num[ a[L] ];///减去原来的计数贡献
                num[ a[L] ]++;
                ans=ans+num[ a[L] ]*num[ a[L] ];///加上新的计数贡献
            }
            while(L<b[i].l)///左指针往右
            {
                ans=ans-num[ a[L] ]*num[ a[L] ];
                num[ a[L] ]--;
                ans=ans+num[ a[L] ]*num[ a[L] ];
                L++;
            }
            while(R<b[i].r)///右指针往右
            {
                R++;
                ans=ans-num[ a[R] ]*num[ a[R] ];
                num[ a[R] ]++;
                ans=ans+num[ a[R] ]*num[ a[R] ];
            }
            while(R>b[i].r)///右指针往左
            {
                ans=ans-num[ a[R] ]*num[ a[R] ];
                num[ a[R] ]--;
                ans=ans+num[ a[R] ]*num[ a[R] ];
                R--;
            }
            b[i].ans=ans;
        }
    
        sort(b+1,b+m+1,cmp2);
        for(int i=1;i<=m;i++)
            printf("%lld
    ",b[i].ans);
    
    
        return 0;
    }
  • 相关阅读:
    HDU 3401 Trade
    POJ 1151 Atlantis
    HDU 3415 Max Sum of MaxKsubsequence
    HDU 4234 Moving Points
    HDU 4258 Covered Walkway
    HDU 4391 Paint The Wall
    HDU 1199 Color the Ball
    HDU 4374 One hundred layer
    HDU 3507 Print Article
    GCC特性之__init修饰解析 kasalyn的专栏 博客频道 CSDN.NET
  • 原文地址:https://www.cnblogs.com/shoulinniao/p/12829251.html
Copyright © 2011-2022 走看看