zoukankan      html  css  js  c++  java
  • 洛谷 P2709 小B的询问

    洛谷

    这题没人用分块吧?

    好像分块比较麻烦,但是我太弱了,不会莫队,就打了一个比较巧妙的分块。

    1.build函数。

    还是像普通分块一样的写,只是我们需要预处理两个数组。


    (f[i][j])表示从开始到第(i)块,颜色为(j)的颜色有多少个(前缀和)。

    这个东西很好处理。

    for (int i=1;i<=num;i++) {
        for (int j=1;j<=n;j++)
        f[i][a[j]]=f[i-1][a[j]];
        for (int j=l[i];j<=r[i];j++)
        f[i][a[j]]++;
    }
    

    (s[i][j])表示第(i)块,到第(j)块的贡献。

    换句话说,就是第i块到第j块之间的答案。也比较好处理。

    假设现在已经处理到了(s[i][j-1]),那么(s[i][j])中的颜色(k),每出现一次,就用一个(tmp[j])数组自加,

    最后假设(s[i][j-1])中颜色k出现次数为(x),那么$$x=f[j-1][k]-f[i-1][k]$$

    (s[i][j])中颜色k出现次数就是(x+tmp[k]).

    那么我们就让$$s[i][j]+=2*(x+tmp[k])+1$$

    为什么呢?例如(tmp[k]=1)时,$$(x+1){x+1}-x2=2×x+1$$

    还要注意一个问题,(tmp)数组要清零,当然不能单纯地使用(memset)(会超时),而是重复一次上面的循环,将它清零。

    for (int i=1;i<=num;i++)
        for (int j=i;j<=num;j++) {
            s[i][j]=s[i][j-1];
            for (int k=l[j];k<=r[j];k++) {
                int p=f[j-1][a[k]]-f[i-1][a[k]];
                s[i][j]+=2*(p+tmp[a[k]])+1;
                tmp[a[k]]++;
            }
            for (int k=l[j];k<=r[j];k++)
            tmp[a[k]]=0;
        }
    

    2.总的代码就是下面这样,很好理解:

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=50010,S=250;
    int n,m,q,a[N],s[S][S];
    int f[S][N],block,num;
    int belong[N],l[S],r[S],tmp[N];
    
    void build()
    {
        block=sqrt(n);
        num=n/block;
        if (n%block) num++;
        for (int i=1;i<=num;i++)
        l[i]=(i-1)*block+1,r[i]=i*block;
        r[num]=n;
        for (int i=1;i<=n;i++)
        belong[i]=(i-1)/block+1;
        for (int i=1;i<=num;i++) {
            for (int j=1;j<=n;j++)
            f[i][a[j]]=f[i-1][a[j]];
            for (int j=l[i];j<=r[i];j++)
            f[i][a[j]]++;
        }
        for (int i=1;i<=num;i++)
        for (int j=i;j<=num;j++) {
            s[i][j]=s[i][j-1];
            for (int k=l[j];k<=r[j];k++) {
                int p=f[j-1][a[k]]-f[i-1][a[k]];
                s[i][j]+=2*(p+tmp[a[k]])+1;
                tmp[a[k]]++;
            }
            for (int k=l[j];k<=r[j];k++)
            tmp[a[k]]=0;
        }
    }
    
    long long ask(int ls,int rs)
    {
        long long ans=0;
        if (belong[ls]==belong[rs]) {
            for (int i=ls;i<=rs;i++) {
                ans+=2*tmp[a[i]]+1;
                tmp[a[i]]++;
            }
            for (int i=ls;i<=rs;i++) tmp[a[i]]=0;
            return ans;
        }
        for (int i=ls;i<=r[belong[ls]];i++) {
            int p=f[belong[rs]-1][a[i]]-f[belong[ls]][a[i]];
            ans+=2*(p+tmp[a[i]])+1;
            tmp[a[i]]++;
        }
        for (int i=l[belong[rs]];i<=rs;i++) {
            int p=f[belong[rs]-1][a[i]]-f[belong[ls]][a[i]];
            ans+=2*(p+tmp[a[i]])+1;
            tmp[a[i]]++;
        }
        for (int i=ls;i<=r[belong[ls]];i++) tmp[a[i]]=0;
        for (int i=l[belong[rs]];i<=rs;i++) tmp[a[i]]=0;
        if (belong[ls]+1==belong[rs]) return ans;
        return ans+s[belong[ls]+1][belong[rs]-1];
    }
    
    int main()
    {
        ios::sync_with_stdio(0);
        cin>>n>>m>>q;
        for (int i=1;i<=n;i++)
        cin>>a[i];
        build();
        while (m--) {
            int ls,rs;
            cin>>ls>>rs;
            cout<<ask(ls,rs)<<endl;
        }
        return 0;
    }
    
  • 相关阅读:
    hihoCoder #1078 : 线段树的区间修改
    hihoCode r#1077 : RMQ问题再临-线段树
    hihoCoder #1070 : RMQ问题再临
    hihoCoder #1068 : RMQ-ST算法(模板)
    LeetCode Valid Sudoku 有效数独
    150 Evaluate Reverse Polish Notation 逆波兰表达式求值
    149 Max Points on a Line 直线上最多的点数
    148 Sort List 链表上的归并排序和快速排序
    147 Insertion Sort List 链表插入排序
    146 LRU Cache 最近最少使用页面置换算法
  • 原文地址:https://www.cnblogs.com/fushao2yyj/p/9512574.html
Copyright © 2011-2022 走看看