cdq分治入门--BZOJ3262: 陌上花开

n<=100000个人,每个人三个属性Ai,Bi,Ci,一个人i的等级为Ai>=Aj,Bi>=Bj,Ci>=Cj的人数,求每个等级有多少人。

裸的三维偏序。按照常规思路,一维排序,一维归并,一维利用单调性或用树状数组维护,这里选择后者。

先按Ai排序,然后在分治过程中,solve(l,mid),solve(mid+1,r),然后考虑(l,mid)对(mid+1,r)答案的贡献,先把这两部分分别按B排序,然后两个指针一起扫,扫的过程中,把C的值作下标丢进树状数组,查询时相当于树状数组前缀和。

思路很好,可样例过不了。

原因:在计算过程中默认后面对前面是没有贡献的,但按Ai排序后,相同的Ai值可能引起后面对前面的贡献。

方法:保证后面对前面无贡献,一开始就先按A再按B最后按C排序即可。

那还有重复的呢?去重呗!

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<stdlib.h>
 4 #include<algorithm>
 5 //#include<iostream>
 6 using namespace std;
 7 
 8 int n,m;
 9 #define maxn 200011
10 int ord[maxn],tmpord[maxn];
11 struct BIT
12 {
13     int a[maxn];
14     BIT() {memset(a,0,sizeof(a));}
15     void add(int x,int v) {for (;x<=n;x+=x&-x) a[x]+=v;}
16     int query(int x) {int ans=0;for (;x;x-=x&-x) ans+=a[x];return ans;}
17 }t;
18 
19 struct Point
20 {
21     int x,y,z,cnt;
22     bool operator < (const Point &b) const
23     {return x<b.x || (x==b.x && y<b.y) || (x==b.x && y==b.y && z<b.z);}
24 }q[maxn],p[maxn];
25 int ans[maxn],ansnum[maxn];
26 void solve(int L,int R)
27 {
28     if (L==R) {ans[L]+=p[L].cnt-1;ord[L]=L;return;}
29     const int mid=(L+R)>>1;
30     solve(L,mid);
31     solve(mid+1,R);
32     int i=L,j=mid+1,k=L;
33     while (i<=mid && j<=R)
34     {
35         if (p[ord[i]].y<=p[ord[j]].y)
36         {
37             tmpord[k++]=ord[i];
38             t.add(p[ord[i]].z,p[ord[i]].cnt);
39             i++;
40         }
41         else
42         {
43             tmpord[k++]=ord[j];
44             ans[ord[j]]+=t.query(p[ord[j]].z);
45             j++;
46         }
47     }
48     for (;j<=R;j++) ans[ord[j]]+=t.query(p[ord[j]].z),tmpord[k++]=ord[j];
49     for (int ii=L;ii<i;ii++) t.add(p[ord[ii]].z,-p[ord[ii]].cnt);
50     for (;i<=mid;i++) tmpord[k++]=ord[i];
51     for (int x=L;x<=R;x++) ord[x]=tmpord[x];
52 }
53 
54 int lisan[maxn];
55 int main()
56 {
57     scanf("%d%d",&n,&m);
58     for (int i=1;i<=n;i++)
59     {
60         scanf("%d%d%d",&q[i].x,&q[i].y,&q[i].z);
61         lisan[i]=q[i].z;
62     }
63     sort(lisan+1,lisan+1+n);
64     for (int i=1;i<=n;i++) q[i].z=lower_bound(lisan+1,lisan+1+n,q[i].z)-lisan;
65     sort(q+1,q+1+n);
66     int tot=0;q[0].x=-0x3f3f3f3f;
67     for (int i=1;i<=n;i++)
68     {
69         if (q[i-1].x==q[i].x && q[i-1].y==q[i].y && q[i-1].z==q[i].z) p[tot].cnt++;
70         else p[++tot]=q[i],p[tot].cnt=1;
71     }
72     solve(1,tot);
73     for (int i=1;i<=tot;i++) ansnum[ans[i]]+=p[i].cnt;
74     for (int i=0;i<n;i++) printf("%d\n",ansnum[i]);
75     return 0;
76 }
View Code

 

转载于:https://www.cnblogs.com/Blue233333/p/7886679.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值