莫队总结&bzoj 2038 小Z的袜子

算法简介

莫队算法,是一种用于解决序列上的问题的离线算法,可以回答对于区间的询问,非常bug。

算法流程

先读入所有的询问,对询问的左端点分块。
在对询问排序,以左端点的块编号为第一关键字,以右端点为第二关键字。
然后遍历询问,同时维护L,R以及答案:对于当前区间,[l,r],不断移动左端点与右端点,直至l==L&&r==R。

算法复杂度

算法复杂度为 O(nn) ,可以用意识流跑一遍:
对于同一个块,R最多移动n次,然而,一共只有 n 个块,所以复杂度就是 O(nn)
对于左端点,先讨论同一个块中的情况,每次移动至多为 n ,于是总复杂度为 O(nn) ,对于不同块中的情况,只有 n 个,即使每次 O(n) 也没关系。
综上,莫队算法的复杂度就是 O(nn)

入门题——小Z的袜子

题目链接
Ti 表示数i在 [L,R] 中出现的次数。
P=C2iC2RL+1=Ti(Ti1)/2(RL+1)(RL)/2=T2iTi(RL+1)(RL)
显然 Ti =R-L+1,所以只要求 T2i 就可以了。
这时我们惊奇的发现,似乎把刚学的莫队算法套上去就可以了!
cnti 表示元素i在当前区间中出现的次数。
每次添加一个元素,就往答案中加 (cnti+1)2cnt2i ,再 cnti++ 这就是刚加入到元素的贡献。
每次删除一个元素,就减掉 cnt2i(cnti1)2 ,再 cnti
每次操作都是 O(1) 的,所以总复杂度是 O(nn)

/**************************************************************
    Problem: 2038
    User: unicornt
    Language: C++
    Result: Accepted
    Time:1540 ms
    Memory:2788 kb
****************************************************************/

#include
#include
#include
using namespace std;
const int M=50005;
typedef long long ll;
struct Query{
    int L,R,id;
}q[M];
int s,col[M];
ll ans[M][2],cnt[M];
bool cmp(Query a,Query b){
    if(a.L/s==b.L/s) return a.Rreturn a.L/ss;
}
int gcd(ll a,ll b){
    return a?gcd(b%a,a):b;
}
int main(){
    int n,m;
    scanf("%d%d",&n,&m);
    s=(int)sqrt(n);
    for(int i=1;i<=n;i++)
        scanf("%d",&col[i]);
    for(int i=0;i<m;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        q[i]=(Query){a,b,i};
    }
    sort(q,q+m,cmp);
    int L=1,R=0;
    ll res=0;
    for(int i=0;i<m;i++){
        while(R<q[i].R){
            R++;
            res+=(cnt[col[R]]+1)*(cnt[col[R]]+1)-cnt[col[R]]*cnt[col[R]];
            cnt[col[R]]++;
        }
        while(L<q[i].L){
            res-=cnt[col[L]]*cnt[col[L]]-(cnt[col[L]]-1)*(cnt[col[L]]-1);
            cnt[col[L]]--;
            L++;
        }
        while(R>q[i].R){
            res-=cnt[col[R]]*cnt[col[R]]-(cnt[col[R]]-1)*(cnt[col[R]]-1);
            cnt[col[R]]--;
            R--;
        }
        while(L>q[i].L){
            L--;
            res+=(cnt[col[L]]+1)*(cnt[col[L]]+1)-cnt[col[L]]*cnt[col[L]];
            cnt[col[L]]++;
        }
        ans[q[i].id][0]=res-R+L-1;
        ans[q[i].id][1]=1LL*(R-L+1)*(R-L);
    }
    for(int i=0;i<m;i++){
        int G=gcd(ans[i][0],ans[i][1]);
        ans[i][0]/=G;
        ans[i][1]/=G;
        if(!ans[i][0]) ans[i][1]=1;
    }
    for(int i=0;i<m;i++)
        printf("%lld/%lld\n",ans[i][0],ans[i][1]);
    return 0;
}

一些题

codeforces 375D Tree and Queries 题解
codeforces 86D power array题解
hdu 4638 Group 题解

你可能感兴趣的:(莫队算法)