首页 > 其他分享 >【普通莫队】2023牛客多校5 A

【普通莫队】2023牛客多校5 A

时间:2023-09-12 09:46:56浏览次数:40  
标签:int sum 多校 tot 牛客 sqrt ans 莫队

简介

莫队算法是由莫涛提出的算法。

在莫涛提出莫队算法之前,莫队算法已经在 Codeforces 的高手圈里小范围流传,但是莫涛是第一个对莫队算法进行详细归纳总结的人。

莫涛提出莫队算法时,只分析了普通莫队算法,但是经过 OIer 和 ACMer 的集体智慧改造,莫队有了多种扩展版本。

莫队算法可以解决一类离线区间询问问题,适用性极为广泛。同时将其加以扩展,便能轻松处理树上路径询问以及支持修改操作。
—— OI-Wiki

普通莫队

例题:给定一个长为n的序列 \(a_1, a_2, ..., a_n\),有m次查询,每次查询求区间 \([l, r]\)中满足: \(l \leq i < j < k \leq r\),\(a_i = a_k > a_j\)的三元组\((i, j, k)\)的个数。

莫队用于处理这样的问题:离线多次查询序列区间,每次查询都需要\(O(n)\)的时间计算答案,但是由区间\([l, r]\)的答案计算\([l + 1, r], [l - 1, r], [l, r + 1], [l, r - 1]\)的答案时只需要\(O(1)\)的时间。

当已知\([l, r]\)的答案,要计算\([l^{'}, r^{'}]\)的答案时,只需将\(l\)转移到\(l^{'}\), \(r\)转移到\(r^{'}\),将区间两个端点看成平面坐标系上的一个点的横纵坐标,转移过程所需要的步数即为两点之间的曼哈顿距离。

由此,我们可以将所有点在平面坐标系中画出来,最佳的转移方案即为一个最小生成树,其中两点之间的距离为曼哈顿距离,转移所需的步数为最小生成树的大小。

考虑对查询区间进行如下的排序方式,可以获得较优的转移顺序:

将序列按照\(\sqrt n\)大小分块,从左到右标上序号,按照查询区间左端点所属分块序号为第一关键字,右端点大小为第二关键字排序,最终在\(n=m\)的情况下计算所有答案的时间复杂度为\(O(n \sqrt n)\)

给出一个不太严谨的证明:

首先分块之后,每个块大的大小为\(\sqrt n\),块的数量为\(\lceil \sqrt n \rceil\),暴力计算每个块的第一个查询区间,每次计算\(O(n)\),共有\(\lceil \sqrt n \rceil\)次,时间复杂度为\(O(n \sqrt n)\)。在同一个块中,右端点单调递增,转移右端点的复杂度为\(O(n)\),左端点每次转移都在同一块中,复杂度为\(O(\sqrt n)\),转移的总数为\(n\)次,总复杂度为\(O(n \sqrt n)\)。

对于上面的例题,首先用树状数组求出每个位置\(i\)前面满足\(a_j < a_i\)的\(j\)的数量,记为\(c_i\)。为了\(O(1)\)转移,还需记录两个数组\(tot_i\)和\(sum_i\),分别表示当前区间中值为\(i\)的位置的数量和总和。

分四种情况:

\(r\)到\(r + 1\),答案增加\(tot_{a_{r + 1}} \cdot c_{r + 1} - sum_{a_{r + 1}}\)

\(l\)到\(l - 1\),答案增加\(sum_{a_{l - 1}} - tot_{a_{l - 1}} \cdot c_{l - 1}\)

\(r\)到\(r - 1\),答案减少\((tot_{a_r} - 1) \cdot c_r - (sum_{a_r} - c_r)\)

\(l\)到\(l + 1\),答案减少\((sum_{a_l} - c_l) - (tot_{a_l} - 1) \cdot c_l\)

代码
#include <bits/stdc++.h>
using namespace std;
typedef long long lld;
const int N = 500005;
struct Que {
    int l, r, num;
    lld ans;
};
int n, m, a[N];
lld c[N], tot[N], sum[N];
vector <int> poss[N];
lld s[N];
int siz, id[N], numb;
Que que[N];
inline int lowbit(int x) {
    return x & -x;
}
void add(int x, int k) {
    for(; x < N; x += lowbit(x)) s[x] += k;
}
int Sum(int x) {
    int summ = 0;
    for(; x; x -= lowbit(x)) summ += s[x];
    return summ;
}
bool cmp1(Que a, Que b) {
    if(id[a.l] == id[b.l]) return a.r < b.r;
    return id[a.l] < id[b.l];
}
bool cmp2(Que a, Que b) {
    return a.num < b.num;
}
int main() {
    // freopen("data.in", "r", stdin);
    ios::sync_with_stdio(false);
    cin >> n >> m;
    siz = (int)sqrt(n); numb = ceil((double)n / (double)siz);
    for(int i = 1; i <= numb; i++) {
        for(int j = (i - 1) * siz + 1; j <= i * siz; j++) {
            id[j] = i;
        }
    }
    for(int i = 1; i <= n; i++) {
        cin >> a[i];
        poss[a[i]].push_back(i);
    }
    for(int i = 1; i <= m; i++) {
        cin >> que[i].l >> que[i].r;
        que[i].num = i;
    }
    for(int i = 1; i <= n; i++) {
        if(poss[i].size()) {
            for(auto j : poss[i]) {
                c[j] = Sum(j);
            }
            for(auto j : poss[i]) {
                add(j, 1);
            }
        }
    }
    sort(que + 1, que + 1 + m, cmp1);
    int l = 1, r = 0; lld ans = 0;
    for(int i = 1; i <= m; i++) {
        int nowl = que[i].l, nowr = que[i].r;
        while(l < nowl) {
            ans = ans - ((sum[a[l]] - c[l]) - (tot[a[l]] - 1) * c[l]);
            tot[a[l]]--; sum[a[l]] -= c[l]; l++;
        }
        while(l > nowl) {
            ans = ans + (sum[a[l - 1]] - tot[a[l - 1]] * c[l - 1]);
            tot[a[l - 1]]++; sum[a[l - 1]] += c[l - 1]; l--;
        }
        while(r < nowr) {
            ans = ans + (tot[a[r + 1]] * c[r + 1] - sum[a[r + 1]]);
            tot[a[r + 1]]++; sum[a[r + 1]] += c[r + 1]; r++;
        }
        while(r > nowr) {
            ans = ans - ((tot[a[r]] - 1) * c[r] - (sum[a[r]] - c[r]));
            tot[a[r]]--; sum[a[r]] -= c[r]; r--;
        }
        que[i].ans = ans;
    }
    sort(que + 1, que + 1 + m, cmp2);
    for(int i = 1; i <= m; i++) {
        cout << que[i].ans << endl;
    }
    return 0;
}

标签:int,sum,多校,tot,牛客,sqrt,ans,莫队
From: https://www.cnblogs.com/mcggvc/p/17695148.html

相关文章

  • 牛客刷题
    #include<stdio.h>intmain(intargc,charconst*argv[]){intk=2000;inti=0;while(k>1){i++;k=k>>1;printf("i=%dk=%d\n",i,k);}printf("%d\n",i);......
  • 莫队算法学习笔记
    莫队普通莫队这个很基础。带修莫队就在普通莫队的基础上加上时间这一维度。[P1903国家集训队]数颜色/维护队列回滚莫队为什么要回滚?因为有些信息不好撤销,比如区间众数。和普通莫队相比较,就是对于每一个块,左端点放在块的右端点处,每次向左扩展,......
  • 牛客练习赛 115 记录
    牛客练习赛115赛时AC题目A.Mountainsequence点击查看代码/*1.根据山峰数列的定义,用排列组合知识去计算即可。*/#include<bits/stdc++.h>#definelllonglongusingnamespacestd;constintmaxn=1e5+5;intt,n;inta[maxn];llans;constintMOD=998244353;......
  • 2023牛客暑期多校训练营4
    A.BoboStringConstruction题意:给定一个01串t,构造一个长度为n的01串s,时的t+s+t中t只在首和尾出现。分析:结论,s取全0或者全1。①假设t全0或者全1,那我s和t取相反的即可。②假设t既包含0又包含1,首先t不可能是s的子串,那我们只需考虑t是否可以由t的后缀加上s再加上t的前缀得......
  • 牛客——SQL263 牛客每个人最近的登录日期(四)
    描述牛客每天有很多人登录,请你统计一下牛客每个日期登录新用户个数,有一个登录(login)记录表,简况如下:iduser_idclient_iddate1212020-10-122322020-10-123122020-10-124222020-10-135122020-10-136312020-10-147412020-10-1......
  • 牛客——SQL254 统计salary的累计和running_total
    描述按照salary的累计和running_total,其中running_total为前N个当前(to_date='9999-01-01')员工的salary累计和,其他以此类推。具体结果如下Demo展示。。CREATETABLEsalaries(emp_noint(11)NOTNULL,salaryint(11)NOTNULL,from_datedateNOTNULL,to_datedate......
  • 牛客——SQL253 获取有奖金的员工相关信息
    描述现有员工表employees如下:emp_nobirth_datefirst_namelast_namegenderhire_date100011953-09-02GeorgiFacelloM1986-06-26100021964-06-02BezalelSimmelF1985-11-21有员工奖金表emp_bonus:emp_noreceviedbtype100012010-01-011......
  • bitset 优化莫队
    题目传送门:Ynoi跳进兔子洞好题!我们观察题目,发现题目让我们求的可以写成:\[(r_1-l_1+1)+(r_2-l_2+1)+(r_3-l_3+1)-3\timessize\]其中:\(size\)是三段中公共颜色的个数问题转移成求三段公共颜色的个数。考虑使用莫队,然后在转换左右边界的时候使用数组记录每个颜色出现几次,然后......
  • 2023牛客暑期多校训练营9
    D.Non-Puzzle:ErrorPermutation题意:给出一个排列,计算其有多少个子区间,满足区间内的第\(i\)个数不是第\(i\)小的数Solution首先明白一点,对于一个数,它的大小排序只会变大而不会变小,变大的要求是后面遇到比它小的数。所以我们可以发现,对于一个数,它会有以下几种情况:1.在开始的......
  • 牛客——SQL166 每天的日活数及新用户占比
    描述用户行为日志表tb_user_logiduidartical_idin_timeout_timesign_cin110190012021-10-3110:00:002021-10-3110:00:090210290012021-10-3110:00:002021-10-3110:00:090310102021-11-0110:00:002021-11-0110:00:421410290012021......