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 }

时间: 2024-08-23 03:54:40

cdq分治入门--BZOJ3262: 陌上花开的相关文章

COGS 577 蝗灾 [CDQ分治入门题]

题目链接 昨天mhr神犇,讲分治时的CDQ分治的入门题. 题意: 你又一个w*w正方形的田地. 初始时没有蝗虫. 给你两个操作: 1. 1 x y z: (x,y)这个位置多了z只蝗虫. 2. 2 x1 y1 x2 y2: 询问(x1,y1)到(x2,y2)这个矩形内的蝗虫数量. 其中 W<=500000,操作数<=200000 . 题解: w范围太大,无法使用二维数据结构. 于是我们可以分治操作. CDQ分治:定义 solve(l,r) 设m=(l+r)/2; 先计算 l-m 修改操作对 m

cdq分治入门--BZOJ1492: [NOI2007]货币兑换Cash

n<=100000天,一开始有s块钱,每天股票A价格ai,B价格bi,每天可以做的事情:卖出股票:按A:B=RTi的比例买入股票.问最后的最大收益.股票可以为浮点数,答案保留三位. 用脚指头想想就知道是:某一天全部买进来,某一天全部卖出去,没有说买一半卖一半的. 那就可以dp了,f(i)表示前i天最大收益,,其中Xi表示用f(i)块钱在第i天能买多少A券,Yi表示f(i)块前第i天买多少B券,可以自己算,n方过不了. 现要找max(Ai*Xj+Bi*Yj),考虑两个状态j,k,j比k优时 整理得

cdq分治入门--BZOJ1176: [Balkan2007]Mokia

对w*w,w<=2000000的矩形,一开始全是0(或一开始全是s),n<=170000个操作,每次操作:矩阵内某点加上一个数,查某一个子矩阵的和,保证修改数<=160000,询问数<=10000. 这还是一个比较明显的三维偏序:时间维,以及x和y.由于现在时间维是一个Ti<Tj,而x和y是要查x1<=x<=x2,y1<=y<=y2,查一个范围答案在归并排序直接查不方便,所以一个询问拆4个,就变成普通的三维偏序了. 1 #include<stdi

【BZOJ3262】陌上花开(CDQ分治)

[BZOJ3262]陌上花开(CDQ分治) 题解 原来放过这道题目,题面在这里 树套树的做法也请点上面 这回用CDQ分治做的 其实也很简单, 对于第一维排序之后 显然只有前面的对后面的才会产生贡献 那么,使用CDQ分治 先分,每次递归子问题 合并的时候每次考虑前面的对于后面的贡献 最后统计一下答案 如果在清空树状数组的时候用了memset会TLE #include<iostream> #include<cstdio> #include<cstdlib> #include

BZOJ3262/洛谷P3810 陌上花开 CDQ分治 三维偏序 树状数组

原文链接http://www.cnblogs.com/zhouzhendong/p/8672131.html 题目传送门 - BZOJ3262 题目传送门 - 落谷P3810 题意 有$n$个元素,第$i$个元素有$a_i$.$b_i$.$c_i$三个属性,设$f(i)$表示满足$a_j\leq a_i$且$b_j\leq b_i$且$c_j\leq c_i$的$j$的数量.对于$d\in [0,n)$,求$f(i)=d$的数量. $n\leq 100000,max\{a_i,b_i,c_i|i

BZOJ3262陌上花开 CDQ分治_BIT+Treap

三个属性, 第一个属性用cdq分治处理, 以第一个属性为关键字从小到大排序, 那么考虑一朵花的等级, 只需考虑排在其前面的花的其他属性(特殊情况是有相同的花,根据题意,对一段相同的花,以排在最后的一朵花的答案为准), 第二三维可以用树状数组加Treap解决, 以每朵花第二属性数值作为位置(因为最大属性k < 2e5, 可以不用离散化, 直接用属性的数值对应树状数组中的下标), 树状数组的每个节点建一颗Treap, 这颗Treap里存的是相应区间里的花的第三个属性, 询问时类似于树状数组求前缀和,

bzoj3262陌上花开 (CDQ,BIT)

题目大意 给定n朵花,每个花有三个属性,定义\(f[i]\)为满足\(a_j \le a_i\)且\(b_j \le b_i\)且\(c_j \le c_i\)的j的数量, 求\(d \in [0,n)\),\(f[i]=d\)的数量 这题是一道CDQ的入门题 CDQ其实本质上是一个分治的过程,需要将询问离线,然后进行分治 一般都是: if (l==r) return; cdq(l,mid); cdq(mid+1,r); 处理左半区间对右半区间的影响 对于这个题,有三维,首先对于第一维进行排序去

BZOJ 3262: 陌上花开 [CDQ分治 三维偏序]

Description 有n朵花,每朵花有三个属性:花形(s).颜色(c).气味(m),又三个整数表示.现要对每朵花评级,一朵花的级别是它拥有的美丽能超过的花的数量.定义一朵花A比另一朵花B要美丽,当且仅当Sa>=Sb,Ca>=Cb,Ma>=Mb.显然,两朵花可能有同样的属性.需要统计出评出每个等级的花的数量. Input 第一行为N,K (1 <= N <= 100,000, 1 <= K <= 200,000 ), 分别表示花的数量和最大属性值. 以下N行,每

BZOJ 3262: 陌上花开 cdq分治 树状数组

https://www.lydsy.com/JudgeOnline/problem.php?id=3262 cdq分治板子题,一维排序,一维分治(cdq里的队列),一维数据结构(树状数组). 学dp优化前来复习--以前好像写过这道题但是没写博客啊--在校oj上写的题都没怎么写博客,追悔莫及 1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #