看到全是线段树或者树状数组写法,就来提供一发全网唯一cdq分治三维偏序解法吧
容易发现,这个题的查询就是对于每个区间l,r,查询有多少个修改区间li,ri与l,r有交集
转化为数学语言,就是查询满足li<=r且ri>=l的修改个数
一个二维偏序问题,但是我们发现,这是个动态插入的二维偏序问题
_(:з」∠)_一时不知所措
再想一想,不妨把时间另开一个维度作为第三维,然后就是这样了
对于每个查询,我们要求出它之前有多少个修改区间与其相交
数学语言:
查询满足li<=r且ri>=l且[li,ri].time<[l,r].time的修改个数
思路清晰明了,而且敲好想,但是实现细节还是比较麻烦的(一部分是因为我的奇葩cdq写法),在代码注释里解释一下(模板这种的就不解释了)
#include<iostream> #include<cstdio> #include<algorithm> #include<vector> using namespace std; const int maxn=1e5+10; struct node{ int a,b,c,q,w; //a,b,c表示三个维度,q记录这个操作是修改还是查询 //w表示这个是否有效(和q差不多,查询是不需要统计的,w=0;修改的w=1) }v[maxn]; int n,m,cnt,tot,c[maxn],ans[maxn]; bool vis[maxn]; bool cmpx(const node &a,const node &b) { return a.a==b.a?(a.b==b.b?a.c<b.c:a.b<b.b):a.a<b.a; } bool cmpy(const node &a,const node &b) { return a.b==b.b?a.c<b.c:a.b<b.b; } int lowbit(int x) { return x&-x; } void add(int x,int ch) { while(x<=n) { c[x]+=ch; x+=lowbit(x); } } int sum(int x) { int ret=0; while(x) { ret+=c[x]; x-=lowbit(x); } return ret; } void cdq(int l,int r) { if(l==r) return; int mid=l+r>>1; cdq(l,mid),cdq(mid+1,r); sort(v+l,v+mid+1,cmpy),sort(v+mid+1,v+r+1,cmpy); int i=l,j=mid+1; for(;j<=r;j++) { while(v[i].b<=v[j].b&&i<=mid) add(v[i].c,v[i].w),i++; if(v[j].q==2) ans[v[j].a]+=sum(n)-sum(v[j].c-1); //统计贡献到ans数组中 } for(j=l;j<i;j++) add(v[j].c,-v[j].w); } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=m;i++) { scanf("%d%d%d",&v[i].q,&v[i].b,&v[i].c); v[i].w=1; if(v[i].q==2) swap(v[i].b,v[i].c),v[i].w=0,vis[i]=1; //标记每个操作是否需要输出 v[i].a=i; } cdq(1,m); //枚举每个操作,需要输出就输出 for(int i=1;i<=m;i++) if(vis[i]) printf("%d\n",ans[i]); return 0; }
原文地址:https://www.cnblogs.com/ivanovcraft/p/9692861.html
时间: 2024-10-10 10:22:10