BZOJ 2038: [2009国家集训队]小Z的袜子(hose)(莫队算法)

题目链接:点击打开链接

题意:给n个袜子, 每个袜子有一个颜色。 m次查询,每次查询给一个区间, 求在区间里的任选一对袜子的颜色相同的概率。

最经典的莫队算法, 莫队算法有两种, 我刚刚学了第一种:分块。

简单来说,假设总区间长度为n,那么将区间分成size = sqrt(n)块,那么每一块的长度为n/size,也是sqrt(n),所以每次只维护一块里的内容和块与块之间的内容,如果改变一个数的值,那么只需要改变其所在的那一块就行了,最多遍历sqrt(n)次,如果要进行区间查询,那么首先要遍历完全在区间中的每一块的信息,复杂度最高O(sqrt(n)), 然后遍历在两边的不完全被包含的部分,也是sqrt(n),所以总的复杂度就是O(sqrt(n))。

细节参见代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<iostream>
#include<string>
#include<vector>
#include<stack>
#include<bitset>
#include<cstdlib>
#include<cmath>
#include<set>
#include<list>
#include<deque>
#include<map>
#include<queue>
#define Max(a,b) ((a)>(b)?(a):(b))
#define Min(a,b) ((a)<(b)?(a):(b))
using namespace std;
typedef long long ll;
const double PI = acos(-1.0);
const double eps = 1e-6;
const int mod = 1000000000 + 7;
const int INF = 1000000000;
const int maxn = 50010;
int T,n,m,unit,a[maxn];
ll vis[maxn];
struct node {
    int l, r, id;
    bool operator < (const node& rhs) const {
        if(l/unit != rhs.l/unit) return l/unit < rhs.l/unit;
        else return r/unit < rhs.r/unit;
    }
}node[maxn];
struct Ans {
    ll a, b;
}ans[maxn];
ll gcd(ll a, ll b) {
    if(b == 0) return a;
    return gcd(b, a%b);
}
void solve() {
    ll cur = 0;
    memset(vis, 0, sizeof(vis));
    int l = 1, r = 0;
    for(int i=0;i<m;i++) {
        int L = node[i].l, R = node[i].r;
        while(r < R) {
            r++;
            cur -= (ll)vis[a[r]] * (vis[a[r]]-1);
            vis[a[r]]++;
            cur += (ll)vis[a[r]] * (vis[a[r]]-1);
        }
        while(r > R) {
            cur -= (ll)vis[a[r]] * (vis[a[r]]-1);
            vis[a[r]]--;
            cur += (ll)vis[a[r]] * (vis[a[r]]-1);
            r--;
        }
        while(l < L) {
            cur -= (ll)vis[a[l]] * (vis[a[l]]-1);
            vis[a[l]]--;
            cur += (ll)vis[a[l]] * (vis[a[l]]-1);
            l++;
        }
        while(l > L) {
            l--;
            cur -= (ll)vis[a[l]] * (vis[a[l]]-1);
            vis[a[l]]++;
            cur += (ll)vis[a[l]] * (vis[a[l]]-1);
        }
        ll len = r - l + 1;
        ans[node[i].id].a = cur;
        ans[node[i].id].b = len * (len-1);
        ll g = gcd(ans[node[i].id].a,ans[node[i].id].b);
        ans[node[i].id].a /= g;
        ans[node[i].id].b /= g;
    }
}
int main() {
    while(~scanf("%d%d",&n,&m)) {
        for(int i=1;i<=n;i++) {
            scanf("%d",&a[i]);
        }
        for(int i=0;i<m;i++) {
            scanf("%d%d",&node[i].l,&node[i].r);
            node[i].id = i;
        }
        unit = floor(sqrt(n));
        sort(node, node+m);
        solve();
        for(int i=0;i<m;i++) {
            printf("%lld/%lld\n",ans[i].a,ans[i].b);
        }
    }
    return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值