学习笔记——CDQ 分治
前言
以前的学弟都学了就我还不会,只能爬了。。。
CDQ 分治
这是一种思想!这是一种思想!这是一种思想!
CDQ 分治是非常优美的过程。当我们考虑序列中的数对计数等问题的时候可以想到用 CDQ 分治。
其过程是:
- 掏出一个序列;
- 从中间剪开;
- 答案分成了 \(3\) 类,一是只在左边的,二是只在右边的,三是左右之间形成的;
- 我们把前两类交给递归,只需要考虑最后一类;
- 这样就可以用奇技淫巧来解决了。
例题
和 \(dp\) 一样,多写点题吧。
放一点东西帮自己思考。
三维偏序的时候,根据排序的那一维来判断双指针中哪个作为主动的那个。
P3810 【模板】三维偏序(陌上花开)
模板题,对第一维排序,然后 cdq 分治下去,能保证第一维左边小于右边。然后对于第二维在分治的时候排序用双指针扫过去。第三维用数据结构什么的维护一下就可以了。
$\texttt{Code}$
#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb push_back
using namespace std;
const int MAXN=2e5+10;
struct BIT{
int tr[MAXN],n;
int lbt(int x){return x&(-x);}
void upd(int x,int v){for(int i=x;i<=n;i+=lbt(i))tr[i]+=v;}
int ask(int x){int ret=0;for(int i=x;i;i-=lbt(i))ret+=tr[i];return ret;}
}T;
struct data{
int a,b,c,v,ans;
void input(){scanf("%d%d%d",&a,&b,&c);v=0;ans=0;}
bool friend operator==(data x,data y){
return (x.a==y.a)&&(x.b==y.b)&&(x.c==y.c);
}
}tmp[MAXN],d[MAXN];
bool cmp1(data x,data y){
if(x.a==y.a){
if(x.b==y.b) return x.c<y.c;
return x.b<y.b;
}return x.a<y.a;
}
bool cmp(data x,data y){
if(x.b==y.b) return x.c<y.c;
return x.b<y.b;
}
void cdq(int l,int r){
if(l==r) return;
int mid=l+r>>1,j=l;
cdq(l,mid);cdq(mid+1,r);
sort(d+l,d+mid+1,cmp);sort(d+mid+1,d+r+1,cmp);
for(int i=mid+1;i<=r;i++){
while(d[j].b<=d[i].b&&j<=mid)
T.upd(d[j].c,d[j].v),j++;
d[i].ans+=T.ask(d[i].c);
}for(int i=l;i<j;i++) T.upd(d[i].c,-d[i].v);
}
int f[MAXN];
int main()
{
int n,k;
scanf("%d%d",&n,&k);T.n=k;
for(int i=1;i<=n;i++) tmp[i].input();
sort(tmp+1,tmp+1+n,cmp1);
int pt=1,m=0;
for(int i=1;i<=n;i=pt){
d[++m]=tmp[i];
while(pt<=n&&tmp[i]==tmp[pt]) d[m].v++,pt++;
}cdq(1,m);
for(int i=1;i<=m;i++)
f[d[i].ans+d[i].v-1]+=d[i].v;
for(int i=0;i<n;i++) printf("%d\n",f[i]);
}
P3157 [CQOI2011]动态逆序对
加一个键值,表示删除的时间,然后我们考虑先求所有逆序对的数目,然后每次减去删去的数所形成的逆序对个数,这个东西可以用 cdq 离线做掉(实际上就是两次三维偏序)。
$\texttt{Code}$
#include<bits/stdc++.h>
#define int long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb push_back
using namespace std;
const int MAXN=1e5+10;
int tr[MAXN],N;
int lbt(int x){return x&(-x);}
void upd(int x,int v){for(int i=x;i<=N;i+=lbt(i))tr[i]+=v;}
int ask(int x){int ret=0;for(int i=x;i;i-=lbt(i))ret+=tr[i];return ret;}
struct node{int val,t,ans;}a[MAXN];
bool cmp(node x,node y){return x.t<y.t;}
bool cmp1(node x,node y){return x.val<y.val;}
int tlm[MAXN],ans[MAXN];
void cdq(int l,int r){
if(l==r) return;int mid=l+r>>1;
cdq(l,mid);cdq(mid+1,r);
sort(a+l,a+mid+1,cmp1);sort(a+mid+1,a+r+1,cmp1);
int i=l,j=mid+1;
for(;i<=mid;i++){
while(a[i].val>a[j].val&&j<=r)
upd(a[j].t,1),j++;
a[i].ans+=ask(N)-ask(a[i].t);
}for(int p=mid+1;p<j;p++) upd(a[p].t,-1);
i=mid,j=r;
for(;j>=mid+1;j--){
while(a[j].val<a[i].val&&i>=l)
upd(a[i].t,1),i--;
a[j].ans+=ask(N)-ask(a[j].t);
}for(int p=mid;p>i;p--) upd(a[p].t,-1);
}
signed main()
{
int m,n;
scanf("%lld%lld",&n,&m);N=max(n,m+1);
for(int i=1;i<=n;i++) scanf("%lld",&a[i].val),a[i].ans=0;
for(int i=1,tt;i<=m;i++) scanf("%lld",&tt),tlm[tt]=i;
for(int i=1;i<=n;i++)
if(tlm[a[i].val]) a[i].t=tlm[a[i].val];
else a[i].t=m+1;
int ans=0;
for(int i=n;i>=1;i--){
ans+=ask(a[i].val);
upd(a[i].val,1);
}memset(tr,0,sizeof(tr));
cdq(1,n);sort(a+1,a+1+n,cmp);
for(int i=1;i<=m;i++){
printf("%lld\n",ans);
ans-=a[i].ans;
}
}