离线分治学习笔记

发布时间 2023-04-16 21:49:34作者: L_fire

分治在数集维护上用处很大,常用的是 cdq 分治,整体二分,线段树分治

cdq 分治

基本思想

  1. 将区间 \(l\)\(r\) 分成 \(l\)\(mid\)\(mid+1\)\(r\)

  2. 递归处理左右两边

  3. 统计左边对右边的贡献

可以解决 \(3\) 类问题

  1. 解决和偏序有关的问题

  2. 1D/1D 动态规划的优化与转移

  3. 一些动态问题转化为静态问题

P3810

先对第一维排序,再分治第二维

回溯时需用双指针统计左边对右边的贡献,流程如下

先对左右两边按第二维分别排序,在左右两边各放一个指针,若右边对应的值大于左边,则将左边的指针向右移一位,直到不能移动为止,可知当右边指针向后移动时,左边的指针及其之前的值第二维都小于新指的值

此时对于每个右指针,左指针及其之前的数都前两维小于右指针,偏序只可能在这之间产生,考虑使用树状数组维护,只需单点加,查询前缀和即可

对于这题,应先排序去重,再进行分治,对于一个 有 \(ans\) 个不同的偏序,\(sum\) 个相同的数,则在
\(ans+sum-1\) 处贡献了 \(sum\) 个值,处理一下即可

点击查看代码
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<vector>
#include<queue>
#include<string>
#include<cstring>
using namespace std;
struct node{
	int a,b,c,num,ans;
}in[200100],sol[200100];
int n,Max,m,rt,id,sum[200100];
int c[200100];
int lowbit(int x)
{
	return x&(-x);
}
void add(int x,int y)
{
	while(x<=Max)
	{
		c[x]+=y;
		x+=lowbit(x);
	}
}
int query(int x)
{
	int sum=0;
	while(x)
	{
		sum+=c[x];
		x-=lowbit(x);
	}
	return sum;
}
bool cmp1(node x,node y)
{
	if(x.a==y.a&&x.b==y.b) return x.c<y.c;
	else if(x.a==y.a) return x.b<y.b;
	return x.a<y.a;
}
bool cmp2(node x,node y)
{
	if(x.b==y.b) return x.c<y.c;
	return x.b<y.b;
}
void cdq(int li,int ri)
{
	if(li==ri) return ;
	int mid=(li+ri)>>1;
	cdq(li,mid);cdq(mid+1,ri);
	sort(sol+li,sol+mid+1,cmp2);
	sort(sol+mid+1,sol+ri+1,cmp2);
	int i=li,j=mid+1;
	for(;j<=ri;j++)
	{
		while(sol[i].b<=sol[j].b&&i<=mid)
		{
            add(sol[i].c,sol[i].num);
			i++;
		}
		sol[j].ans+=query(sol[j].c);
	}
	for(int k=li;k<i;k++)
	{
		add(sol[k].c,-sol[k].num);
	}
	rt=id=0;
}
int main()
{
	cin>>n>>Max;
	for(int i=1;i<=n;i++)
	{
		cin>>in[i].a>>in[i].b>>in[i].c;
	}
	sort(in+1,in+1+n,cmp1);
	int top=0;
	for(int i=1;i<=n;i++)
	{
		top++;
		if(in[i].a!=in[i+1].a||in[i].b!=in[i+1].b||in[i].c!=in[i+1].c)
		{
			m++;
			sol[m]={in[i].a,in[i].b,in[i].c,top,0};
			top=0;
		}
	}
	cdq(1,m);
	for(int i=1;i<=m;i++)
	{
		sum[sol[i].ans+sol[i].num-1]+=sol[i].num;
	}
	for(int i=0;i<n;i++)
	{
		cout<<sum[i]<<endl;
	}
	return 0;
}