思路:
看没人写莫队算法自己就写了一下
写了一下发现和不同的莫队算法不一样
普通的莫队算法不支持修改操作
而这里因为询问的k会变
你之前的区间的值都会受影响
问了一下大佬结果学到了带修改莫队
对于普通的莫队我们再加上一个变量
t[a[x]]表示a[x]出现的次数
再新增一个变量now,在每次询问中对于now<k的情况我们就让
对于now>k的情况进行相反操作即可
同时对于每个询问的排序要加第三关键字如果右边界处于同一块就按k大小进行排序
这里分块size大小用的是n的2/3次方
听说时间复杂度是 (但具体还没搞明白)
之后这题就能过了(因为主席树也练了一下后面附上主席树代码(有注释))
树状数组也写了一下
发现三个算法里最快的还是树状数组
莫队是最慢的
AC代码(莫队)
#include <iostream> #include <algorithm> #include <math.h> using namespace std; const int N=1e5+10; typedef long long ll; struct node{ int l,r,k,i; }q[N]; ll a[N]; int pos[N]; int t[N]; int add(int x,int num){ t[a[x]]++; if(a[x]<=num)return 1; else return 0; } int del(int x,int num){ t[a[x]]--; if(a[x]<=num)return 1; else return 0; } int ans[N]; int main() { ios::sync_with_stdio(false); int n,m; cin>>n>>m; int siz=pow(n,0.66666666); for(int i=1;i<=n;i++){ cin>>a[i]; pos[i]=i/siz; } for(int i=1;i<=m;i++){ cin>>q[i].l>>q[i].r>>q[i].k; q[i].i=i; } sort(q+1,q+1+m,[](node x,node y){ if(pos[x.l]!=pos[y.l])return pos[x.l]<pos[y.l]; else if(pos[x.r]!=pos[y.r])return pos[x.r]<pos[y.r]; else return x.k<y.k; }); int l=1,r=0; int cnt=0,now=0; for(int i=1;i<=m;i++){ while(r<q[i].r)cnt+=add(++r,now); while(l>q[i].l)cnt+=add(--l,now); while(r>q[i].r)cnt-=del(r--,now); while(l<q[i].l)cnt-=del(l++,now); while(now<q[i].k)cnt+=t[++now]; while(now>q[i].k)cnt-=t[now--]; ans[q[i].i]=cnt; } for(int i=1;i<=m;i++)cout<<ans[i]<<endl; return 0; }
主席树:
#include <iostream> #include <algorithm> using namespace std; const int N=1e5+10; struct node{ int l,r,sum; }tree[N*40];//对应的树是权值线段树 int n,m; int root[N];//根节点 int a[N]; int tot=0; void add(int l,int r,int pre,int &now,int p){ tree[++tot]=tree[pre];//让新版本的线段树等于之前版本的线段树 now=tot; tree[now].sum++;//开始对新版本的线段树进行修改 if(l==r)return; int mid=(l+r)>>1; if(p<=mid)add(l,mid,tree[pre].l,tree[now].l,p);//如果p<=mid就只修改左边 else add(mid+1,r,tree[pre].r,tree[now].r,p);//不然修改右边 } int query(int l,int r,int L,int R,int ql,int qr){ int mid=(l+r)>>1; if(ql==l&&qr==r)return tree[R].sum-tree[L].sum;//返回这段区间出现的值的次数 if(qr<=mid)return query(l,mid,tree[L].l,tree[R].l,ql,qr);//如果询问的右界值在mid左边,就往左边去扫 else if(ql>mid)return query(mid+1,r,tree[L].r,tree[R].r,ql,qr);//如果询问的左界值在mid右边,就直接去右边扫 else return query(l,mid,tree[L].l,tree[R].l,ql,mid)+query(mid+1,r,tree[L].r,tree[R].r,mid+1,qr); //不然扫两边把sum值加起来,注意最后的ql,qr都是要变的 } int main() { ios::sync_with_stdio(false); cin>>n>>m; for(int i=1;i<=n;i++){ cin>>a[i]; add(1,1e5,root[i-1],root[i],a[i]); } for(int i=1;i<=m;i++){ int l,r,q; cin>>l>>r>>q; cout<<query(1,1e5,root[l-1],root[r],1,q)<<endl; } return 0; }