天天看点

莫队总结&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=∑C2iC2R−L+1=∑Ti∗(Ti−1)/2(R−L+1)∗(R−L)/2=∑T2i−∑Ti(R−L+1)∗(R−L)

显然 ∑Ti =R-L+1,所以只要求 ∑T2i 就可以了。

这时我们惊奇的发现,似乎把刚学的莫队算法套上去就可以了!

用 cnti 表示元素i在当前区间中出现的次数。

每次添加一个元素,就往答案中加 (cnti+1)2−cnt2i ,再 cnti++ 这就是刚加入到元素的贡献。

每次删除一个元素,就减掉 cnt2i−(cnti−1)2 ,再 cnti−− 。

每次操作都是 O(1) 的,所以总复杂度是 O(nn−√) 。

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

#include<cmath>
#include<cstdio>
#include<algorithm>
using namespace std;
const int M=;
typedef long long ll;
struct Query{
    int L,R,id;
}q[M];
int s,col[M];
ll ans[M][],cnt[M];
bool cmp(Query a,Query b){
    if(a.L/s==b.L/s) return a.R<b.R;
    return a.L/s<b.L/s;
}
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=;i<=n;i++)
        scanf("%d",&col[i]);
    for(int i=;i<m;i++){
        int a,b;
        scanf("%d%d",&a,&b);
        q[i]=(Query){a,b,i};
    }
    sort(q,q+m,cmp);
    int L=,R=;
    ll res=;
    for(int i=;i<m;i++){
        while(R<q[i].R){
            R++;
            res+=(cnt[col[R]]+)*(cnt[col[R]]+)-cnt[col[R]]*cnt[col[R]];
            cnt[col[R]]++;
        }
        while(L<q[i].L){
            res-=cnt[col[L]]*cnt[col[L]]-(cnt[col[L]]-)*(cnt[col[L]]-);
            cnt[col[L]]--;
            L++;
        }
        while(R>q[i].R){
            res-=cnt[col[R]]*cnt[col[R]]-(cnt[col[R]]-)*(cnt[col[R]]-);
            cnt[col[R]]--;
            R--;
        }
        while(L>q[i].L){
            L--;
            res+=(cnt[col[L]]+)*(cnt[col[L]]+)-cnt[col[L]]*cnt[col[L]];
            cnt[col[L]]++;
        }
        ans[q[i].id][]=res-R+L-;
        ans[q[i].id][]=LL*(R-L+)*(R-L);
    }
    for(int i=;i<m;i++){
        int G=gcd(ans[i][],ans[i][]);
        ans[i][]/=G;
        ans[i][]/=G;
        if(!ans[i][]) ans[i][]=;
    }
    for(int i=;i<m;i++)
        printf("%lld/%lld\n",ans[i][],ans[i][]);
    return ;
}
           

一些题

codeforces 375D Tree and Queries 题解

codeforces 86D power array题解

hdu 4638 Group 题解