首页 > 其他分享 >Algorithm 3 - 数据结构

Algorithm 3 - 数据结构

时间:2023-01-01 15:44:58浏览次数:48  
标签:移动 Algorithm 复杂度 ques 端点 数据结构 莫队 ll

数据结构是该好好补补拉。

1. 线段树

2. 平衡树

3. 莫队

3.1 普通莫队

莫队解决的问题一般是区间查询,并且可以离线。利用一种排序区间的方式,保证暴力移动最有指针的复杂度。

我们考虑将序列分块,设一个点 \(i\) 在块 \(b_i\) 内。

假设我们有 \(q\) 个询问,对其进行这样的简单排序:

定义 \(\text{cmp}\) 函数,比较两个区间时,先比较左端点的块编号;相等则比较右端点编号。

排序完成后,我们通过暴力移动指针的方式解决问题。

复杂度?

观察左端点的移动,每次都在同一个块中移动,换块的部分是 \(O(n)\) 的也忽略不计,总的移动次数是 \(O(n^{1.5})\)。

观察右端点的移动,当左端点在同一块内时,右端点递增,那么每个块最多 \(O(n)\),总的也是 \(O(n^{1.5})\)。这就是莫队的精髓。

3.1 莫队二次离线

最近新 get 到的一个东东。

我们知道,设暴力移动指针的复杂度是 \(x\),那么莫队可以做到 \(O(n^{1.5} \times x)\) 的复杂度。如果 \(x\) 过大,有可能无法接受。

那么莫队二次离线可以做到什么效果呢?实际上,为了严格一点,设 修改一个元素 的复杂度是 \(x\),查询一个元素 的复杂度是 \(y\)(两者需要结合使用,必须相对而言,不能各取最小值),那么可以做到的是 \(O(n \times x + n ^{1.5} \times y)\)。


开始讲具体怎么做。我们设一个元素 \(x\) 对区间 \((l,r)\) 的贡献是 \(f(l,r,x)\)。

移动指针时,我们可能需要求出这样的 \(f(l,r,x)\)。我们以把右端点向右移动一格为例,贡献是:\(f(l,r-1,r)\)。如果能拆,我们可以把它拆成 \(f(1,r-1,r) - f(1,l-1,r)\)。(不一定是减号)

现在我们 二次离线,这些移动端点的贡献我们再离线下来去计算。因为被我们拆成了前缀,所以好算了很多,复杂度也轻松做到上述的。

但是!我们发现移动了 \(O(n^{1.5})\) 次指针,也就是如果全部离线下来用 vector 存,那么空间寄了。

但是!每次移动指针都是一个区间,所以存储这个区间。就做完了 /cf

现在我们上代码讲解 P4887

// 首先为了降低查询的复杂度,考虑维护 tong 表示一个数的贡献
// a^b=c 等价于 b=a^c
// 我们假设 c 是合法数字,那么加入一个数的时候去枚举 c 暴力改桶
// 修改复杂度虽然高,但是查询成了 O(1)
// 完美解决
ll n,m,k,block,bl[100005];
struct Ques{
    ll l,r,id,ans;
}ques[100005];
bool operator< (Ques A,Ques B){
    if(bl[A.l]==bl[B.l]) return A.r<B.r;
    else return bl[A.l]<bl[B.l];
}
ll popcount(ll x){
    ll res=0;
    while(x){
        res+=(x&1);
        x>>=1;
    }
    return res;
}
ll pre[100005], tong[100005], ans[100005], a[100005];
vector<tuple<ll,ll,ll,ll> >vec[100005];
void solve(){
    n=read(), m=read(), k=read();
    if(k>=14){
        for(ll i=1;i<=m;i++){
            cout<<0<<endl;
        }
        return;
    }
    block = n / sqrt(m);
    for(ll i=1;i<=n;i++) bl[i]=(i-1)/block+1;
    for(ll i=1;i<=n;i++) a[i]=read();
    for(ll i=1;i<=m;i++)
        ques[i].l=read(), ques[i].r=read(), ques[i].id=i;
    sort(ques+1, ques+m+1);
    vector<ll>allow;
    for(ll i=0;i<16384;i++){
        if(popcount(i)==k){
            allow.emplace_back(i);
        }
    } // 预处理合法数字
    ll l=1, r=0;
    for(ll i=1;i<=n;i++){
        pre[i]=tong[a[i]];
        for(auto x: allow) ++tong[a[i]^x];
    }
    memset(tong, 0, sizeof(tong));
    for(ll i=1;i<=m;i++){
        if(l>ques[i].l) vec[r].emplace_back(ques[i].l, l-1, i, 1);
        while(l>ques[i].l) --l, ques[i].ans-=pre[l];
		//如果左端点需要向左移动,那么 [l-1, ques[i].l] 这一段的贡献都得加上,对应前缀为 r,但是要减掉 pre 预处理的前缀(对前面的贡献多加了)
        if(r<ques[i].r) vec[l-1].emplace_back(r+1, ques[i].r, i, -1);
        while(r<ques[i].r) ++r, ques[i].ans+=pre[r];
		// 向上面这样分析就好了

        if(l<ques[i].l) vec[r].emplace_back(l, ques[i].l-1, i, -1);
        while(l<ques[i].l) ques[i].ans+=pre[l], ++l;
        if(r>ques[i].r) vec[l-1].emplace_back(ques[i].r+1, r, i, 1);
        while(r>ques[i].r) ques[i].ans-=pre[r], --r;
    } // 核心,重点
    for(ll i=1;i<=n;i++){
        for(auto x: allow) ++tong[a[i]^x];
        for(auto P: vec[i]){
            for(ll j=get<0>(P); j<=get<1>(P); j++){
                if(j<=i && k==0) ques[get<2>(P)].ans+=get<3>(P)*(tong[a[j]]-1);
                else ques[get<2>(P)].ans+=get<3>(P)*(tong[a[j]]);
            } 
			// 大力枚举区间修改贡献
        }
    }
    for(ll i=1;i<=m;i++) ques[i].ans += ques[i-1].ans; // 注意我们只维护了答案的差分,最后求一遍前缀和
    for(ll i=1;i<=m;i++)
        ans[ques[i].id]=ques[i].ans;
    for(ll i=1;i<=m;i++) printf("%lld\n", ans[i]);
}

标签:移动,Algorithm,复杂度,ques,端点,数据结构,莫队,ll
From: https://www.cnblogs.com/BreakPlus/p/17018130.html

相关文章

  • 数据结构-堆
    手写堆堆排序问题描述:输入一个长度为n的整数数列,从小到大输出前m小的数。解决思路:与上面的写的思路一样实现对应的代码即可代码:#include<iostream>using......
  • Algorithm 2 - 一些数论/组合计数知识
    0.一些前置知识莫比乌斯函数:定义\(\mu(x)\)为:当\(x\)含平方因子,则\(\mu(x)=0\);否则设其有\(p\)个质因子,\(\mu(x)=(-1)^p\)。特别的,\(\mu(1)=1\)。莫比乌斯......
  • 数据结构与算法(C语言 严蔚敏)二
    前言有误的地方还请大家指出来,我会一一改正,也会在评论区置顶更正的记录;如果是因为不同的教材导致的错误,请把教材名、著作者、版次一并提供,供大家一起督促一起学习,本篇参考......
  • 数据结构之队列
    1.队列介绍队列是一个有序列表,可以用数组或者链表来实现。遵循先入先出的原则。即先存入队列的数据,要先取出。后存入的要后取出。2.数组模拟队列思路队列本身是有......
  • 数据结构与算法学习一:学习前的准备,数据结构的分类,数据结构与算法的关系,实际编程中遇到
    文章目录​​前言​​​​一、数据结构​​​​1.1线性结构​​​​1.2非线性结构​​​​二、数据结构与算法​​​​2.1两者之间的关系​​​​2.2两者重要性​​​​......
  • Redis学习二、 基础篇 5大基本数据结构、数据库底层操作、持久化功能、主从模式,所有常
    一、Redis数据结构数据模型Redis的外围由一个键、值映射的字典构成。与其他非关系型数据库主要不同在于:Redis中值的类型[1]不仅限于字符串,还支持如下抽象数据类型:字符串......
  • 华北水利水电大学 2023考研 967数据结构真题
    评价:整体很简单,类似于大学期末考试难度华北水利水电大学2023考研967数据结构真题回忆版1 选择题10道 20分 给一个abaabaa,请问next数组? 循环队列为空的条件是?r......
  • 数据结构第四节——哈希表
    数据结构第四节——哈希表初遇哈希,甚感懵比...哈希表:用一个好的哈希函数(哈希函数由自己定义),将一些关键字从一个大集合中映射到一个小集合中,然后将这个小集合的信息储......
  • 数据结构第六节——堆
    数据结构第六节——堆特殊的二叉树——堆~~~堆:每个节点的值总是不大于或不小于其父节点的值的完全二叉树。知识点小汇:堆的根节点总是最小/最大值。大根堆:父节点......
  • Redis数据结构存储系统:第四章:底层实现原理
    应用场景:设置限制的优惠活动的信息;一些及时需要更新的数据,积分排行榜;手机验证码的时间;限制网站访客访问频率;Redis数据结构存储系统:第四章:底层实现原理Redis以什么形......