zoto
Code1
树状数组套动态开点权值线段树
效仿HH的项链,维护右端点,询问需要排序
#include<bits/stdc++.h>
using namespace std;
template <class T=int> T rd()
{T res=0;char ch=getchar();while(!isdigit(ch)) ch=getchar();while( isdigit(ch)) res=(res<<1)+(res<<3)+(ch^48),ch=getchar();return res;
}
const int N=100010;
int a[N],n,m,ans[N],last[N];
struct nodeq
{int l,r,L,R,id;bool operator<(const nodeq& o)const{return r<o.r;}
}q[N];
struct node
{int l,r,v;
}tree[N*200];
int rt[N],cnt;
void update(int &u,int l,int r,int pos,int v)
{if(!u) tree[u=++cnt]={0,0,0};tree[u].v+=v;if(l==r) return;int mid=l+r>>1;if(pos<=mid) update(tree[u].l,l,mid,pos,v);if(pos>mid) update(tree[u].r,mid+1,r,pos,v);
}
int query(int u,int l,int r,int L,int R)
{if(!u) return 0;if(L<=l&&r<=R) return tree[u].v;int mid=l+r>>1;int v=0;if(L<=mid) v+=query(tree[u].l,l,mid,L,R);if(R>mid) v+=query(tree[u].r,mid+1,r,L,R);return v;
}
int lowbit(int x){return x&-x;}
void add(int k,int pos,int v)
{for(;k<=n;k+=lowbit(k)) update(rt[k],0,100000,pos,v);
}
int ask(int k,int L,int R)
{int ans=0;for(;k;k-=lowbit(k)) ans+=query(rt[k],0,100000,L,R);return ans;
}void init()
{for(int i=1;i<=100000;i++) last[i]=0;for(int i=1;i<=n;i++) rt[i]=0;cnt=0;for(int i=0;i<=cnt;i++) tree[i].l=tree[i].r=tree[i].v=0;
}
int main()
{int Tc=rd();while(Tc--){init();n=rd(),m=rd();for(int i=1;i<=n;i++) a[i]=rd();for(int i=1;i<=m;i++){int x0=rd(),y0=rd(),x1=rd(),y1=rd();q[i]={x0,x1,y0,y1,i};}sort(q+1,q+1+m);int k=1;for(int i=1;i<=m;i++){for(;k<=q[i].r;k++){int v=a[k];if(last[v]) add(last[v],v,-1);add(k,v,1);last[v]=k;}ans[q[i].id]=ask(q[i].r,q[i].L,q[i].R)-ask(q[i].l-1,q[i].L,q[i].R);}for(int i=1;i<=m;i++) printf("%d\n",ans[i]);}
}
Code2
树状数组套动态开点权值线段树
校dl的写法,不需要对询问进行排序
#include<bits/stdc++.h>
using namespace std;
template <class T=int> T rd()
{T res=0;char ch=getchar();while(!isdigit(ch)) ch=getchar();while( isdigit(ch)) res=(res<<1)+(res<<3)+(ch^48),ch=getchar();return res;
}
const int N=100010;
int a[N],n,m,ans[N],last[N];
struct nodeq
{int l,L,R,id;
};
vector<nodeq> q[N];
struct node
{int l,r,v;
}tree[N*200];
int rt[N],cnt,lim;
void update(int &u,int l,int r,int pos,int v)
{if(!u) u=++cnt;tree[u].v+=v;if(l==r) return;int mid=l+r>>1;if(pos<=mid) update(tree[u].l,l,mid,pos,v);if(pos>mid) update(tree[u].r,mid+1,r,pos,v);//tree[u].v=tree[tree[u].l].v+tree[tree[u].r].v;
}
int query(int u,int l,int r,int L,int R)
{if(!u) return 0;if(L<=l&&r<=R) return tree[u].v;int mid=l+r>>1;int v=0;if(L<=mid) v+=query(tree[u].l,l,mid,L,R);if(R>mid) v+=query(tree[u].r,mid+1,r,L,R);return v;
}
int lowbit(int x){return x&-x;}
void add(int k,int pos,int v)
{for(;k<=n;k+=lowbit(k)) update(rt[k],0,lim,pos,v);
}
int ask(int k,int L,int R)
{int ans=0;for(;k;k-=lowbit(k)) ans+=query(rt[k],0,lim,L,R);return ans;
}
void init()
{for(int i=0;i<=lim;i++) last[i]=0;for(int i=0;i<=n;i++) rt[i]=0;for(int i=0;i<=cnt;i++) tree[i].l=tree[i].r=tree[i].v=0;cnt=0;for(int i=0;i<=n;i++) q[i].clear();
}
int main()
{int Tc=rd();while(Tc--){init();n=rd(),m=rd();for(int i=1;i<=n;i++) a[i]=rd();lim=*max_element(a+1,a+1+n);for(int i=1;i<=m;i++){int x0=rd(),y0=rd(),x1=rd(),y1=rd();q[x1].push_back({x0,y0,y1,i});}for(int i=1;i<=n;i++){if(last[a[i]]) add(last[a[i]],a[i],-1);add(i,a[i],1);last[a[i]]=i;for(auto t:q[i])ans[t.id]=ask(i,t.L,t.R)-ask(t.l-1,t.L,t.R);}for(int i=1;i<=m;i++) printf("%d\n",ans[i]);}
}
Code3
std写法
首先来看一个子问题:给一个数组,初始所有位置全是0,每次单点加一或减一,询问区间有多少个位置不为0。也许你很快会给出一个修改O(logn)查询O(logn)的优秀做法。那么如果强制规定修改O(1),查询能做到什么效率?我们去考虑分块,维护两个数组num[i]和sum[i]分别维护第i位置上的值,以及第i块内的值不为0的位置个数。那么每次单点修改只需要修改num[i]和sum[i/block_size]两个位置的值,然后查询的时候需要O(sqrtn)效率的去查询区间所覆盖的完整块的值以及区间两端散块的值(散块值可以没有)。这显然是一个修改O(1),查询O(sqrtn)的做法。
然后考虑这个题,我们发现可以用莫队去维护询问的区间,即x坐标。y(fx)维度单独拎出来就变成了上述子问题。我们会神奇的发现,这样的复杂度是:O(nsqrt(n)1+m1sqrt(n))的,莫队单次修改时O(sqrtn)的复杂度遇上了值域上O(1)的修改,莫队单次查询时O(1)的复杂度遇上了值域查询时O(sqrt(n))的复杂度。
#include<bits/stdc++.h>using namespace std;
using ll=long long;
template <class T=int> T rd()
{T res=0;char ch=getchar();while(!isdigit(ch)) ch=getchar();while( isdigit(ch)) res=(res<<1)+(res<<3)+(ch^48),ch=getchar();return res;
}
const int N=100010;
int a[N],n,m;int b[N],sz;
struct nodeq
{int l,r,L,R,id;bool operator<(const nodeq&o)const{if(b[l]==b[o.l]){if(b[l]&1)return r<o.r;elsereturn r>o.r;}return b[l]<b[o.l];}
}q[N];
int num[N],sum[N],ans[N];
void add(int k){if(++num[k]==1) sum[b[k]]++;}
void sub(int k){if(--num[k]==0) sum[b[k]]--;}
int query(int k)
{int res=0;for(int i=1;i<b[k];i++) res+=sum[i];for(int i=(b[k]-1)*sz+1;i<=k;i++) res+=(num[i]>=1);return res;
}
int main()
{int Tc=rd();while(Tc--){n=rd(),m=rd(),sz=313;for(int i=1;i<=n;i++) a[i]=rd();for(int i=1;i<=n;i++) sum[i]=num[i]=0;for(int i=1;i<=n;i++) b[i]=(i-1)/sz+1;for(int i=1;i<=m;i++){int x0=rd(),y0=rd(),x1=rd(),y1=rd();q[i]={x0,x1,y0,y1,i};}sort(q+1,q+1+m);int l=1,r=0;for(int i=1;i<=m;i++){while(l<q[i].l) sub(a[l++]);while(l>q[i].l) add(a[--l]);while(r<q[i].r) add(a[++r]);while(r>q[i].r) sub(a[r--]);ans[q[i].id]=query(q[i].R)-query(q[i].L-1);}for(int i=1;i<=m;i++) printf("%d\n",ans[i]);}
}
Code4
把种类查询转换偏序查询的常规操作:指向上一个出现的位置当且仅当上一次出现的位置在区间外面时计算贡献
cdq分治+bit
对于每个询问的右端点也看作一个时间轴,如果该点可能对询问产生贡献,那么下一个这个点的横坐标严格大于询问右端点。
a1≤a1b1≤b2c1>c2a_1\leq a_1\\ b_1\leq b_2 \\c_1>c_2a1≤a1b1≤b2c1>c2
cdq分治转化为平面二维数点,然后差分+bit即可统计。
对于每个询问的左端点也看作一个时间轴,如果该点可能对询问产生贡献,那么前一个这个点的横坐标严格小于询问左端点。
本质都种类→\to→偏序
#include<bits/stdc++.h>
using namespace std;
template <class T=int> T rd()
{T res=0;char ch=getchar();while(!isdigit(ch)) ch=getchar();while( isdigit(ch)) res=(res<<1)+(res<<3)+(ch^48),ch=getchar();return res;
}
const int N=100010;
int a[N],n,m,ans[N],last[N];
int ne[N],pos[N];
struct nodeq
{int op;int a,b,c;int fg,id;
}q[N<<4];
int fw[N];
int lowbit(int x){return x&-x;}
void update(int k,int v){for(;k<=n+1;k+=lowbit(k)) fw[k]+=v;}
int qsum(int k){int v=0;for(;k;k-=lowbit(k)) v+=fw[k];return v;}void solve(int l,int r)
{if(l>=r) return;int mid=l+r>>1;solve(l,mid),solve(mid+1,r);int i=l;for(int j=mid+1;j<=r;j++){while(i<=mid&&q[i].b<=q[j].b) {if(q[i].op==0) update(q[i].c,1);i++;}if(q[j].op==1) ans[q[j].id]+=q[j].fg*(qsum(n+1)-qsum(q[j].c));}while(i>l) {--i;if(q[i].op==0) update(q[i].c,-1);}inplace_merge(q+l,q+mid+1,q+r+1,[](const nodeq&x,const nodeq&y){return x.b<y.b||x.b==y.b&&x.a<y.a;});
}
int main()
{int Tc=rd();while(Tc--){n=rd(),m=rd();for(int i=1;i<=n;i++) a[i]=rd();for(int i=1;i<=m;i++) ans[i]=0;for(int i=0;i<=100000;i++) pos[a[i]]=n+1;for(int i=n;i>=1;i--) {ne[i]=pos[a[i]];pos[a[i]]=i;}int cnt=0;for(int i=1;i<=n;i++) q[++cnt]={0,i,a[i],ne[i]};for(int i=1;i<=m;i++){int x0=rd(),y0=rd(),x1=rd(),y1=rd();q[++cnt]={1,x0-1,y0-1,x1,1,i};q[++cnt]={1,x0-1,y1,x1,-1,i};q[++cnt]={1,x1,y0-1,x1,-1,i};q[++cnt]={1,x1,y1,x1,1,i};}sort(q+1,q+1+cnt,[](const nodeq&x,const nodeq&y){return x.a<y.a||x.a==y.a&&x.b<y.b;});solve(1,cnt);for(int i=1;i<=m;i++) printf("%d\n",ans[i]);}
}
Code5
和Code4基本一样
#include<bits/stdc++.h>
using namespace std;
template <class T=int> T rd()
{T res=0;char ch=getchar();while(!isdigit(ch)) ch=getchar();while( isdigit(ch)) res=(res<<1)+(res<<3)+(ch^48),ch=getchar();return res;
}
const int N=100010;
int a[N],n,m,ans[N];
int last[N],pos[N];
struct nodeq
{int op;int a,b,c;int fg,id;
}q[N<<4];
int fw[N];
int lowbit(int x){return x&-x;}
void update(int k,int v){if(!k) return fw[k]+=v,void();for(;k<=n;k+=lowbit(k)) fw[k]+=v;}
int qsum(int k){int v=fw[0];for(;k;k-=lowbit(k)) v+=fw[k];return v;}void solve(int l,int r)
{if(l>=r) return;int mid=l+r>>1;solve(l,mid),solve(mid+1,r);int i=l;for(int j=mid+1;j<=r;j++){while(i<=mid&&q[i].b<=q[j].b) {if(q[i].op==0) update(q[i].c,1);i++;}if(q[j].op==1) ans[q[j].id]+=q[j].fg*qsum(q[j].c);}while(i>l) {--i;if(q[i].op==0) update(q[i].c,-1);}inplace_merge(q+l,q+mid+1,q+r+1,[](const nodeq&x,const nodeq&y){return x.b<y.b||x.b==y.b&&x.a<y.a;});
}
int main()
{int Tc=rd();while(Tc--){n=rd(),m=rd();for(int i=1;i<=n;i++) a[i]=rd();for(int i=1;i<=m;i++) ans[i]=0;for(int i=0;i<=100000;i++) pos[a[i]]=0;for(int i=1;i<=n;i++) {last[i]=pos[a[i]];pos[a[i]]=i;}int cnt=0;for(int i=1;i<=n;i++) q[++cnt]={0,i,a[i],last[i]};for(int i=1;i<=m;i++){int x0=rd(),y0=rd(),x1=rd(),y1=rd();q[++cnt]={1,x0-1,y0-1,x0-1,1,i};q[++cnt]={1,x0-1,y1,x0-1,-1,i};q[++cnt]={1,x1,y0-1,x0-1,-1,i};q[++cnt]={1,x1,y1,x0-1,1,i};}sort(q+1,q+1+cnt,[](const nodeq&x,const nodeq&y){return x.a<y.a||x.a==y.a&&x.b<y.b;});solve(1,cnt);for(int i=1;i<=m;i++) printf("%d\n",ans[i]);}
}