首页 > 其他分享 >杂数据结构选做

杂数据结构选做

时间:2024-05-30 22:33:47浏览次数:16  
标签:node suf 选做 return int mid 数据结构 las

杂数据结构选做

持续更新ing...

更新多少取决于我卷了多少

似乎都是比较基础的东西,但是我数据结构太菜了,没办法 ╮(╯_╰)╭

#9016. CodeChef MINXORSEG

有两种做法,我敲的后一种

第一种

先不考虑范围问题,考虑现在有三个点\(u,v,w\),若它们的\(lcp\)为\(l\),那么考虑第\(l+1\)位,根据鸽巢原理,必有两个点的第\(l+1\)位相等,设为\(u\)和\(v\),那么显然选这两个点的配对比选\((u/v,w)\)的配对更优

根据这点,我们考虑建\(trie\),然后将每个点都挂到它在\(trie\)树上经过的点上,因为树高为\(\log W\),所以共会挂\(N\log W\)个点,而\(trie\)树上的每个点中挂着的所有点组成的点对都可以成为上文的\((u,v)\)

但是显然不能暴力的找所有点对,所以把挂着的点按编号排序后,相邻两点组成一个点对即可,这样显然最优

那么我们现在就有\(N\log W\)个点对

再来考虑范围限制\([l,r]\),对于点对\((u,v)\),其合法当且仅当\(u,v\in[l,r]\),那么若我们把所有点对放到二维平面上,这就是一个二维空间数点问题,扫描线+树状数组即可

第二种

这种应该要比上一种好写一些,但是其实都挺好写的

其实两种做法差不多?

还是根据第一种中提到的,考虑\(lcp\),又结合第一种中提到的贪心,发现其实可以不用把所有点对都这样提出来

考虑直接贪心的对每个点\(x\)找到\(lcp\geq i\)的\(y\)(\(y<x\)),然后查询答案时也是扫描线+树状数组维护即可

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+5;
int n,q,a[N],p[N];
int b,to[N][30];
bool cmp(int x,int y){
    if((a[x]>>b)==(a[y]>>b)) return x<y;
    return (a[x]>>b)<(a[y]>>b);
}
struct node{
    int l,r,id;
    bool operator < (const node &other)const{
        return r<other.r;
    }
}cn[N];
int c[N],ans[N];
void add(int x,int y){ for(x=n-x+1;x<=n;x+=x&-x) c[x]=min(c[x],y); }
int ask(int x){
    int ans=1e9;
    for(x=n-x+1;x;x-=x&-x) ans=min(ans,c[x]);
    return ans;
}
int main(){
    scanf("%d%d",&n,&q);
    for(int i=1;i<=n;++i) scanf("%d",&a[i]),p[i]=i,c[i]=1e9;
    for(b=0;b<30;++b){
        stable_sort(p+1,p+n+1,cmp);
        for(int i=2;i<=n;++i) if((a[p[i]]>>b)==(a[p[i-1]])>>b) to[p[i]][b]=p[i-1];
    }
    for(int i=1;i<=q;++i) scanf("%d%d",&cn[i].l,&cn[i].r),cn[i].id=i;
    sort(cn+1,cn+q+1);
    for(int i=1,k=1;i<=n;++i){
        for(int j=0;j<30;++j) if(to[i][j]) add(to[i][j],a[i]^a[to[i][j]]);
        while(k<=q&&cn[k].r==i) ans[cn[k].id]=ask(cn[k].l),++k;
    }
    for(int i=1;i<=q;++i) printf("%d\n",ans[i]);
    return 0;
}

P8078 [WC2022] 秃子酋长

从\(luogu\)的\(tj\)区偷的 如果在分治结构上很难快速合并某些信息,我们就可以利用分块来做所以如果在分治结构上很难快速合并某些信息,我们就可以利用分块来做。反过来,我们如果想要用任何的分治结构做这道题,我们都需要发现一个性质,支持某种程度上比暴力更快地合并区间信息的性质。

考虑如果我们知道了\([l,mid]\)的答案以及\([mid+1,r]\)的答案,能否合并

将\([l,mid]\)中以及\([mid+1,r]\)的数按权值从小到大排好序,考虑\(a_i\)后面是\(a_j\),那么记录\(ri_i=j\),\(le_j=i\)

若\(i,j\in[l,mid]\),那么若\([mid+1,r]\)中存在\(k\)使得\(a_k\in[a_i,a_j]\),也即\(a_k\)最终会插入\(a_i,a_j\)中间,那么手摸一下可以发现,代价从\(|i-j|\)变成了\(k-i+k-j\),也即增加了\(2k-2\max(i,j)\),若插入的不止有\(k\),依旧还是有变化的权值中与\(i,j\)有关的部分为\(-2\max(i,j)\)

若\(i\in[l,mid]\)且\(a_i\)是这段区间内最大的那个,如果\([mid+1,r]\)中存在\(k\)使得\(a_k>a_i\)也即\(a_k\)最终会放到\(a_i\)的右边,可以发现贡献最终增加了\(k-i\),若有多个放在\(a_i\)的右边,依旧可以发现变化的权值中与\(i\)相关的值依旧是\(-i\)

对于\(a_i\)是最小的那个同理,贡献和第二种情况相同

当\(i,j\in[mid+1,r]\)时,第一种情况对应的贡献为\(2min(i,j)\),第二种和第三种为\(i\)

那么就可以猫树分治\(O(N\log^2N)\)的做了

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=5e5+5;
int n,m,a[N],p[N];
struct node{ int l,r,id; };
bool cmp1(node a,node b){ return a.l<b.l; }
bool cmp2(node a,node b){ return a.r>b.r; }
ll ans[N];
vector<node> cn;
ll suf[N],pre[N],c[N];
queue<int> op;
void add(int x,int y){ if(abs(x)>N) return ; (x<0)&&(x+=n+1),op.push(x); for(;x<=n;x+=x&-x) c[x]+=y; }
ll ask(int x){
    ll ans=0;
    for((x<0)&&(x+=n+1);x;x-=x&-x) ans+=c[x];
    return ans;
}
void clear(){
    while(!op.empty()){
        int x=op.front(); op.pop();
        for(;x<=n;x+=x&-x) c[x]=0;
    }
}
int le[N],ri[N],pos[N],A,B;
void work(int tl,int tr,int &A,int k){
    for(int x=tl,y=tr;x;x=ri[x]){
        while(y&&a[y]<a[x]) (x==tl)&&(A=min(A,k*y)),y=ri[y];
        while(y&&a[x]<a[y]&&(!ri[x]||a[y]<a[ri[x]])) pos[x]=min(pos[x],k*y),y=ri[y];
    }
}
void Val(int now,int &A,int k,int k1=1,bool flag1=1,bool flag2=1){
    if(flag1){
        if(le[now]) add(-k*pos[le[now]],2*k*k1*abs(max(-k*le[now],-k*now))); else add(-k*A,k*k1*now);
    }
    if(flag2){
        if(ri[now]) add(-k*pos[now],2*k*k1*abs(max(-k*ri[now],-k*now))); else add(-k*pos[now],k*k1*now);
    }
}
void work1(int now,int &A,int k){
    Val(now,A,k,-1);
    if(le[now]) ri[le[now]]=ri[now],(pos[le[now]]==1e9)?(pos[le[now]]=pos[now]):((pos[now]!=1e9)&&(pos[le[now]]=abs(min(-k*pos[le[now]],-k*pos[now])))); else (A==1e9)?(A=pos[now]):((pos[now]!=1e9)&&(A=abs(min(-k*A,-k*pos[now]))));
    if(ri[now]) le[ri[now]]=le[now];
    if(le[now]) Val(le[now],A,k,1,0,1);
    else if(ri[now]) Val(ri[now],A,k,1,1,0);

}
void solve(int l,int r,vector<node> que,bool flag){
    if(l==r) return ;
    int mid=l+r>>1; vector<node> q1,q2,q3;
    for(auto x:que){
        if(x.r<=mid) q1.push_back(x);
        else if(x.l>mid) q2.push_back(x);
        else q3.push_back(x);
    }
    solve(l,mid,q1,0),solve(mid+1,r,q2,1);
    for(auto x:q3) ans[x.id]=suf[x.l]+pre[x.r];
    set<int> s; A=B=1e9;
    for(int i=mid;i>=l;--i) s.insert(a[i]),pos[i]=1e9,le[i]=ri[i]=0;
    int las=0,stl=0,str=0; for(auto x:s) le[p[x]]=p[las],(las)&&(ri[p[las]]=p[x]),las=x,(!stl)&&(stl=p[x]);
    s.clear(); for(int i=mid+1;i<=r;++i) s.insert(a[i]),pos[i]=1e9,le[i]=ri[i]=0;
    las=0; for(auto x:s) le[p[x]]=p[las],(las)&&(ri[p[las]]=p[x]),las=x,(!str)&&(str=p[x]);
    work(stl,str,A,1),work(str,stl,B,-1),sort(q3.begin(),q3.end(),cmp1);
    if(B<0) B=-B;
    for(int i=mid+1;i<=r;++i) if(pos[i]<0) pos[i]=-pos[i];
    clear(),add(A,-stl);
    for(int now=l;now<=mid;++now){
        if(ri[now]) add(pos[now],-2*max(ri[now],now)); else add(pos[now],-now);
    }
    for(int now=l,i=0;now<=mid;++now){
        for(;i<q3.size()&&q3[i].l==now;++i) ans[q3[i].id]+=ask(q3[i].r);
        work1(now,A,-1);
    }
    sort(q3.begin(),q3.end(),cmp2),clear();
    add(-B,str);
    for(int now=mid+1;now<=r;++now){
        if(ri[now]) add(-pos[now],2*min(ri[now],now)); else add(-pos[now],now);
    }
    for(int now=r,i=0;now>mid;--now){
        for(;i<q3.size()&&q3[i].r==now;++i) ans[q3[i].id]+=ask(-q3[i].l);
        work1(now,B,1);
    }
    if(!flag){//suf
        set<int> s;
        for(int i=r;i>=l;--i){
            suf[i]=i==r?0:suf[i+1];
            auto it=s.lower_bound(a[i]);
            if(it!=s.end()){
                if(it!=s.begin()) suf[i]-=abs(p[*it]-p[*prev(it)]);
                suf[i]+=p[*it]-i;
            }
            if(it!=s.begin()) suf[i]+=p[*prev(it)]-i;
            s.insert(a[i]);
        }
    }else{//pre
        set<int> s;
        for(int i=l;i<=r;++i){
            pre[i]=i==l?0:pre[i-1];
            auto it=s.lower_bound(a[i]);
            if(it!=s.end()){
                if(it!=s.begin()) pre[i]-=abs(p[*it]-p[*prev(it)]);
                pre[i]+=i-p[*it];
            }
            if(it!=s.begin()) pre[i]+=i-p[*prev(it)];
            s.insert(a[i]);
        }
    }
}
int main(){
    scanf("%d%d",&n,&m),cn.resize(m);
    for(int i=1;i<=n;++i) scanf("%d",&a[i]),p[a[i]]=i;
    for(int i=0;i<m;++i) scanf("%d%d",&cn[i].l,&cn[i].r),cn[i].id=i+1;
    solve(1,n,cn,0);
    for(int i=1;i<=m;++i) printf("%lld\n",ans[i]);

    return 0;
}

标签:node,suf,选做,return,int,mid,数据结构,las
From: https://www.cnblogs.com/LuoyuSitfitw/p/18223390

相关文章

  • 数据结构学习笔记-快速排序
    快速排序的算法设计与分析问题描述:设计并分析快速排序【算法设计思想】选择基准值:从待排序数组中选择一个元素作为基准值(pivot)。在这个示例中,选择了数组中的最后一个元素作为基准值。分割数组:将数组分割为两部分,小于等于基准值的元素放在基准值的左边,大于基准值的元素放在右......
  • 优化Python中的数据结构与算法(指南)
    ......
  • JavaDS-学习数据结构之如果从零开始手搓顺序表,顺带学习自定义异常怎么用!
    前言笔者开始学习数据结构了,虽然笔者已经会用了,不管是C++中的stl亦或是Java中的集合,为了算法比赛多少都突击过,但只知其然而不知其所以然,还是会限制发展的,因此,笔者写下这篇博客.内容是手搓一个顺序表.顺带加一点异常的使用,大伙看个乐子就好了.有错误直接私信喷我就......
  • TBL的基础数据结构
    局部类型:在哪里定义的变量,就是那一块的局部变量。生命域就是Events里的大括号内。直接按照数据类型数据名;的形式定义。全局类型:全局变量,在定义的时候要在数据类型的前面加上global,表示这个变量叫做全局变量。Global数据类型变量名;时间序列类型:Series<数据类型>变量名;可以回......
  • 数据结构之栈(Java,C语言的实现)以及相关习题巩固
    目录栈概念以及代码实现例题232.用栈实现队列1614.括号的最大嵌套深度234.回文链表1614.括号的最大嵌套深度LCR123.图书整理I206.反转链表402.移掉K位数字844.比较含退格的字符串LCR036.逆波兰表达式求值[面试题03.01.三合一](栈概念以及代码实现栈是仅限于在......
  • 数据结构 顺序表(C语言 与 Java实现)以及部分练习题
    目录数据结构数组(顺序表)特点使用Java实现更高级的数组C语言实现总结优点缺点例题26.删除有序数组中的重复项1.两数之和27.移除元素153.寻找旋转排序数组中的最小值485.最大连续1的个数414.第三大的数2656.K个元素的最大和LCP06.拿硬币2057.值相等的最小索引26.删......
  • 数据结构学习笔记-冒泡排序
    冒泡排序的算法设计与分析问题描述:设计并分析冒泡排序算法【算法设计思想】遍历数组,从第一个元素到倒数第二个元素(因为最后一个元素不需要再比较,它已经是最大的了)。在每次遍历过程中,再次遍历未排序部分的元素(从第一个到当前未排序部分的末尾),比较相邻的两个元素,如果顺序不正确......
  • 07-图4 哈利·波特的考试(浙大数据结构PTA习题)
    07-图4哈利·波特的考试        分数25        作者 陈越        单位 浙江大学哈利·波特要考试了,他需要你的帮助。这门课学的是用魔咒将一种动物变成另一种动物的本事。例如将猫变成老鼠的魔咒是haha,将老鼠变成鱼的魔咒是hehe等等。反方向变化......
  • 【数据结构】探索树中的奇妙世界
    专栏介绍:哈喽大家好,我是野生的编程萌新,首先感谢大家的观看。数据结构的学习者大多有这样的想法:数据结构很重要,一定要学好,但数据结构比较抽象,有些算法理解起来很困难,学的很累。我想让大家知道的是:数据结构非常有趣,很多算法是智慧的结晶,我希望大家在学习数据结构的过程是一种愉......
  • 【数据结构实验】迷宫问题——线性表
    目录实验目的实验内容(题目) 实验环境 程序代码实验分析 实验目的掌握并理解线性表的存储结构定义,包括线性表的顺序存储结构与链式存储结构。学习并掌握线性表的基本操作实现,如插入、删除、查找、遍历等基本运算。明确线性表的存储结构特点,包括它们的时间和空间复杂......