cdq分治学习笔记
题面传送门
这道题就是\(cdq\)分治板子题。
首先排\(x\)消去一维偏序。
然后先分治子序列,再分治当前序列。
分治时对当前序列两部分分别进行\(y\)排序。
\(cdq\)分治的核心是左序列向右序列算贡献。
考虑枚举右序列每一个元素,同时在左序列维护一个指针,指针左侧的\(y\)值小于等于当前右序列\(y\)值。
易得左指针单调不降。所以将\(z\)加入树状数组即可维护。
代码实现:
#include<cstdio>
#include<algorithm>
#define max(a,b) ((a)>(b)?(a):(b))
using namespace std;
int n,k,z,f[200039],ans[100039],tot[200039],maxn,head,now;
struct yyy{int x,y,z,w,tot;}s[100039],a[100039];
inline bool cmp1(yyy x,yyy y){return (x.x==y.x)?((x.y==y.y)?x.z<y.z:x.y<y.y):x.x<y.x;}
inline bool cmp2(yyy x,yyy y){return x.y==y.y?x.z<y.z:x.y<y.y;}
inline void get(int x,int y){while(x<=k) f[x]+=y,x+=x&-x;}
inline int find(int x){int ans=0;while(x) ans+=f[x],x-=x&-x;return ans;}
inline void solve(int x,int y){
if(x==y) return;
int m=x+y>>1;
solve(x,m);solve(m+1,y);
sort(s+x,s+m+1,cmp2);sort(s+m+1,s+y+1,cmp2);
int l=x,r;
for(r=m+1;r<=y;r++){
while(l<=m&&s[l].y<=s[r].y)
get(s[l].z,s[l].w),l++;
s[r].tot+=find(s[r].z);
}
for(r=x;r<l;r++) get(s[r].z,-s[r].w);
}
int main(){
freopen("1.in","r",stdin);
register int i;
scanf("%d%d",&n,&k);
for(i=1;i<=n;i++) scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
sort(a+1,a+n+1,cmp1);
for(i=2;i<=n+1;i++){
now++;
if(a[i].x!=a[i-1].x||a[i].y!=a[i-1].y||a[i].z!=a[i-1].z)s[++head]=a[i-1],s[head].w=now,now=0;
}
solve(1,head);
for(i=1;i<=head;i++) tot[s[i].tot+s[i].w-1]+=s[i].w;
for(i=0;i<n;i++) printf("%d\n",tot[i]);
}

浙公网安备 33010602011771号