avatar
fireworks99
keep hungry keep foolish

洛谷 p1494 小Z的袜子

Description

详见链接: https://www.luogu.org/problemnew/show/P1494

莫队算法

学习一个算法,除了理解它的原理之外,要清楚它的特点,以此来思考适合解决何种问题

莫队算法:区间各元素属性和(累积)查询问题

此题更新操作

分母就是n * n - n(表示两两袜子之间的随机组合),分子是一个累加和 - n,累加的内容是该区间内每种颜色i出现次数sum[i]的平方

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define ll long long
using namespace std;
const int N = 50010;

int n, m;///n个元素,m个询问;
int num, bel[N];///分块个数,每块标号
int a[N], sum[N];///元素值与其在相应区间上出现次数
ll ans;///当前区间查询所得答案

struct edge
{
    int l, r, id;
    ll fro, bac;
}e[N];

bool cmp(edge a, edge b)
{
    return bel[a.l] == bel[b.l] ? a.r < b.r : a.l < b.l;
}

bool cmp_id(edge a, edge b)
{
    return a.id < b.id;
}

ll gcd(ll a, ll b)
{
    return b == 0 ? a : gcd(b, a % b);
}

void update(int x, int change)
{
    ans -= sum[ a[x] ] * sum[ a[x] ];///减去原来的答案
    sum[ a[x] ] += change;           ///更新
    ans += sum[ a[x] ] * sum[ a[x] ];///加上更新后的答案
}

int main()
{
    scanf("%d%d", &n, &m);

    ///1.输入元素并分块
    num = sqrt(n);
    for(int i = 1; i <= n; ++i)///下标需从1开始
    {
        scanf("%d", &a[i]);
        bel[i] = i / num + 1;
    }

    ///2.输入查询区间并记下id并排序
    for(int i = 0; i < m; ++i)
    {
        scanf("%d%d", &e[i].l, &e[i].r);
        e[i].id = i;
    }
    sort(e, e + m, cmp);

    ///3.移动指针更新所查询区间
    int l = 1, r = 0;
    for(int i = 0; i < m; ++i)
    {
        while(l < e[i].l)
            update(l, -1), ++l;
        while(l > e[i].l)
            update(l - 1, 1), --l;
        while(r < e[i].r)
            update(r + 1, 1), ++r;
        while(r > e[i].r)
            update(r, -1), --r;
        if(e[i].l == e[i].r)
        {
            e[i].fro = 0;
            e[i].bac = 1;
            continue;
        }
        e[i].fro = ans - (e[i].r - e[i].l + 1);
        e[i].bac = 1LL * (e[i].r - e[i].l + 1) * (e[i].r - e[i].l);
        ll tem = gcd(e[i].fro, e[i].bac);
        e[i].fro /= tem;
        e[i].bac /= tem;
    }

    ///4.排回原序输出答案
    sort(e, e + m, cmp_id);
    for(int i = 0; i < m; ++i)
        cout << e[i].fro << '/' << e[i].bac << '\n';
    return 0;
}

更多关于此题:https://www.cnblogs.com/Paul-Guderian/p/6933799.html

Site by Baole Zhao | Powered by Hexo | theme PreciousJoy