BZOJ 2308 小Z的袜子 (%%莫队)

题目链接:https://www.lydsy.com/JudgeOnline/problem.php?id=2038

注:现在老版bzoj关闭了注册,大家需要修改自己的hosts文件,然后取新版bzoj注册,再去老版的做题

注册方式:https://www.lydsy.com/JudgeOnline/wttl/thread.php?tid=5671

在修改hosts文件时,需要权限,提高权限的操作如下:右键 —> 属性 —> 安全 —> 高级 —> 更改权限 —> 添加 —> 选择主体—> 高级 —> 立即查找 —> 找到当前所用用户 —> 确定 —> 将权限都选上    OK

在网上好不容易找到的莫队当年的解题报告:https://wenku.baidu.com/view/18ffb44476c66137ee061952.html

先 %%%% 一发莫队大神

小Z的袜子这题是一道询问统计型问题,它没有让你做出修改等操作,因此,如何高效的进行区间查询操作,就是莫队算法了,从朴素的暴力法来看,最大的问题在于,我们会遇到多个区间互相重合的情况,传统的线段树在最劣情况下仍会TLE,所以我们就要搞一些骚操作了,对于区间端点进行更新,并同时将序列Z划分位最上尽量少的不升(降)子序列。

关于如何slove这一部分操作,借用一下别人的讲解:

 

我们设想有一条数轴:

技术分享图片

当curL < L 时,我们当前curL是已经处理好的了。所以remove时先去除当前curL再++

当curL > L 时,我们当前curL是已经处理好的了。所以 add  时先--再加上改后curL

当curR > R 时,我们当前curR是已经处理好的了。所以remove时先去除当前curR再--

当curR < R 时,我们当前curR是已经处理好的了。所以 add  时先++再加上改后curR

 

AC代码:

//看命题报告时还算看懂了他的意思,就是这代码....
#include <bits/stdc++.h>
using namespace std;

const int N = 50005;
typedef long long ll;

int n, m, pos[N], c[N];
ll s[N], ans;

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

struct data{
    int l, r, id;
    ll a, b;
}a[N];

bool cmp(data a, data b){
    if(pos[a.l] == pos[b.l]) return a.r < b.r;
    return a.l < b.l;
}

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

void init()
{
    scanf("%d %d", &n, &m);
    for(int i = 1; i <= n; ++i)
        scanf("%d", &c[i]);
    int block = int(sqrt(n));
    for(int i = 1; i <= n; ++i)
        pos[i] = (i-1)/block+1;     //将n个数分成sqrt(n)块
    for(int i = 1; i <= m; ++i){
        scanf("%d %d", &a[i].l, &a[i].r);
        a[i].id = i;
    }
}

void update(int p, int add)     //对于区间端点的更新
{
    ans -= sqr(s[c[p]]);
    s[c[p]] += add;
    ans += sqr(s[c[p]]);
}

void solve()
{
    for(int i = 1, l = 1, r = 0; i <= m; ++i){
        for(; r < a[i].r; r++)  update(r+1, 1);
        for(; r > a[i].r; r--)  update(r, -1);
        for(; l < a[i].l; l++)  update(l, -1);
        for(; l > a[i].l; l--)  update(l-1, 1);
        if(a[i].l == a[i].r){
            a[i].a = 0;
            a[i].b = 1;
            continue;
        }
        a[i].a = ans - (a[i].r - a[i].l + 1);
        a[i].b = (ll)(a[i].r - a[i].l + 1) * (a[i].r - a[i].l);
        ll k = gcd(a[i].a, a[i].b);
        a[i].a /= k;
        a[i].b /= k;
    }
}

int main()
{
    init();
    sort(a+1, a+m+1, cmp);  //对询问区间的排序
    solve();
    sort(a+1, a+m+1, cmp_id);
    for(int i = 1; i <= m; ++i){
        cout << a[i].a << "/" << a[i].b << '\n';
    }
    return 0;
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值