Loading

学习笔记——CDQ 分治

前言

以前的学弟都学了就我还不会,只能爬了。。。

CDQ 分治

这是一种思想!这是一种思想!这是一种思想!

CDQ 分治是非常优美的过程。当我们考虑序列中的数对计数等问题的时候可以想到用 CDQ 分治。

其过程是:

  1. 掏出一个序列;
  2. 从中间剪开;
  3. 答案分成了 \(3\) 类,一是只在左边的,二是只在右边的,三是左右之间形成的;
  4. 我们把前两类交给递归,只需要考虑最后一类;
  5. 这样就可以用奇技淫巧来解决了。

例题

\(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;
	}
}




JOISC 2014 Day3


posted @ 2022-01-23 21:40  ZCETHAN  阅读(51)  评论(0编辑  收藏  举报