Ⅰ、预备知识
整体二分???
Ⅱ、抛出问题
我们先来看一道洛谷的模板题
题目背景
这是一道模板题
可以使用bitset(不会),CDQ分治,K-DTree(不会)等方式解决。
题目描述
有(n)个元素,第(i)个元素有(a_i)、(b_i)、(c_i)三个属性,设(f(i))表示满足(a_jleq a_i)且(b_jleq b_i)且(c_jleq c_i)的(j)的数量。
对于(din[0, n)),求(f(i)=d)的数量
输入输出格式
输入格式:
第一行两个整数(n、k),分别表示元素数量和最大属性值。
之后(n)行,每行三个整数(a_i)、(b_i)、(c_i),分别表示三个属性值。
输出格式:
输出(n)行,第(d+1)行表示(f(i)=d)的(i)的数量。
输入输出样例
输入样例#1:
10 3
3 3 3
2 3 3
2 3 1
3 1 1
3 1 2
1 3 1
1 1 2
1 2 2
1 3 2
1 2 1
输出样例#1:
3
1
3
0
1
0
1
0
0
1
说明:
(1leq Nleq100000,1leq kleq200000)
Ⅲ、分析问题
CDQ分治,有国家队某巨佬发明(仿佛是插头dp的论文作者???),主要用于解决带修改,查询,可排序序列的一系列问题,仅可支持离线操作
CDQ分治的主要步骤有以下几点:
1、读入(废话)
1、将已经读入好的数据按照某关键字排序
2、设当前区间为([l,r]),递归处理左区间([l,mid])和右区间([mid+1,r]),计算左区间的修改操作对右区间的影响(一般用树状数组等数据结构维护)
3、清除数据结构内的修改数据
本题又叫三维偏序问题,是CDQ分治的经典题型
先按照第一维(即(a_i))排序,这样就将问题转化到了二维
设当前区间为([l,r])
讲([l,mid])和([mid+1,r])分别按照第二维排序,此时在左区间中的(a)均小于有区间中的(a)(保证第一维),设左区间已访问到(pl),右区间已访问到(pr)((lleq plleq mid,mid+1leq prleq r))
当(b[pl]<=b[pr])时(保证第二维),即将(pl)点的(c)值加入树状数组
统计比(pr)点的(c)值小或等于的点的数量(保证第三维)
详见代码
#include<bits/stdc++.h>
#define ll long long
#define INF 2147483647
#define mem(i,j) memset(i,j,sizeof(i))
#define F(i,j,n) for(register int i=j;i<=n;i++)
#define lowbit(i) i&(-i)//树状数组
using namespace std;
struct hahaha{
int x,y,z,ans,cnt;//x,y,z分别对应a,b,c;ans表示题目中的f(i),即三维都小于等于i的数量,cnt表示x,y,z相等的点的数量,若只出现一次,则cnt=1
}f[100010],s[100010];//f为输入数据,s为处理后数据
int nn,n,m,ans[100010],c[200010];//c是树状数组上的点,ans为最终答案
inline int read(){
int datta=0;char chchc=getchar();bool okoko=0;
while(chchc<'0'||chchc>'9'){if(chchc=='-')okoko=1;chchc=getchar();}
while(chchc>='0'&&chchc<='9'){datta=datta*10+chchc-'0';chchc=getchar();}
return okoko?-datta:datta;
}
inline bool cmpx(hahaha a,hahaha b){//以x为第一关键字排序
return a.x==b.x?a.y==b.y?a.z<b.z:a.y<b.y:a.x<b.x;
}
inline bool cmpy(hahaha a,hahaha b){//以y为第一关键字排序
return a.y==b.y?a.z<b.z:a.y<b.y;
}
inline void add(int x,int v){//树状数组修改
for(int i=x;i<=m;i+=lowbit(i))
c[i]+=v;
}
inline int ask(int x){//树状数组查询
int res=0;
for(int i=x;i;i-=lowbit(i))
res+=c[i];
return res;
}
class CDQ_DC{//之所以用class写是为了装逼
private:
public:
inline void CDQ(int l,int r){
if(l==r)//边界条件
return ;
int mid=(l+r)>>1;
CDQ(l,mid);
CDQ(mid+1,r);//递归处理左右区间
sort(s+l,s+mid+1,cmpy);
sort(s+mid+1,s+r+1,cmpy);//按y排序
int pl=l,pr=mid+1;
while(pr<=r){
while(pl<=mid&&s[pl].y<=s[pr].y)
add(s[pl].z,s[pl].cnt),pl++;//加点
s[pr].ans+=ask(s[pr].z);//处理pr的ans
pr++;
}
F(i,l,pl-1)
add(s[i].z,-s[i].cnt);//清空树状数组
}
}C;
int main(){
nn=read();m=read();
F(i,1,nn)
f[i].x=read(),f[i].y=read(),f[i].z=read();
sort(f+1,f+nn+1,cmpx);//按x排序
int ct=0;
F(i,1,nn){
ct++;
if(f[i].x!=f[i+1].x||f[i].y!=f[i+1].y||f[i].z!=f[i+1].z){
s[++n]=f[i];
s[n].cnt=ct;//处理数据
ct=0;
}
}
C.CDQ(1,n);
F(i,1,n)
ans[s[i].ans+s[i].cnt-1]+=s[i].cnt;//处理最后答案
F(i,0,nn-1)
printf("%d
",ans[i]);
return 0;
}