首页 > 其他分享 >cdq分治总结

cdq分治总结

时间:2024-08-09 20:49:57浏览次数:12  
标签:总结 rr int ll 分治 ++ maxn cdq

\(cdq\) 分治是一种离线分治算法,可以将动态问题改变为静态问题,不适用于强制在线。其实现时通常将需要进行的操作存进一个

结构体,然后对这些操作进行分治。

打 \(cdq\) 分治时一个直观的感受就是很好想思路,但就是不知道怎么打。。。

它一共有三个需要干的

  • 1 找到范围中点 \(mid\) ,递归解决 \((l,mid)\)

  • 2 递归解决 \((mid+1,r)\)

  • 3 找出 \(mid\) 左边的修改对 \(mid\) 右面的查询的影响,计入答案

\(cdq\) 分治的主要作用是解决偏序问题,所以只要一道题的询问信息有明显的比较关系,我们就可以考虑用 \(cdq\) 解决,所以

经常会在一些树套树的题的题解区看到有 \(dalao\) 用 \(cdq\) 碾过去

模板

这里的板子是 \(cdq\) 内归并排序的板子和配队的树状数组的板子,好像要快一点?

\(cdq\) 内的 \(if,else\) 应根据题目适当修改

点击查看代码
struct tree
{
	inline int lowbit(int x){return x&-x;}
	
	inline void add(int x,int y)
	{
		while(x<=n)
		{
			if(vis[x]!=dfn) vis[x]=dfn,c[x]=0;
			c[x]+=y;
			x+=lowbit(x);
		}
	}
	
	inline int query(int x)
	{
		int temp=0;
		while(x)
		{
			if(vis[x]==dfn)temp+=c[x];
			x-=lowbit(x);
		}
		return temp;
	}	
}e;

inline void cdq(int l,int r)
{
	if(l==r) return;
	int mid=l+r>>1;
	cdq(l,mid),cdq(mid+1,r);
	dfn++;
	int ll=l,rr=mid+1;
	for(int i=l;i<=r;i++)
	{
		if(ll<=mid&&q[ll].x<=q[rr].x||rr>r)
		{
			if(q[ll].opt==0)e.add(q[ll].y,-q[ll].xy);
			a[i]=q[ll];	
			ll++; 
		}
		else
		{
			if(q[rr].opt==1)ans[q[rr].id]=min(ans[q[rr].id],e.query(q[rr].y)+q[rr].xy);
			a[i]=q[rr];
			rr++;
		}
	}
	for(int i=l;i<=r;i++) q[i]=a[i];
}

二维偏序

P3374 【模板】树状数组 1

这个题实际上就是一个二维偏序问题,我们考虑对一个点 \(x\) 有贡献的操作有什么,是比它时间早的且坐标靠前的点

所以这里的两维分别是时间,坐标。

点击查看代码
#include<bits/stdc++.h>
#define int long long
const int maxn=5e5+10;
using namespace std;
struct lsx{int x,c,opt,cnt,ct;}q[maxn*3],t[maxn*3];
int n,m,ct,a[maxn],cnt,ans[maxn],s[maxn];
bool cmp(lsx a,lsx b){return a.x!=b.x?a.x<b.x:a.opt>b.opt;}

void cdq(int l,int r)
{
	if(l==r) return ;
	int mid=l+r>>1,temp=0;
	for(int i=l;i<=r;i++)
		if(q[i].cnt<=mid&&q[i].opt) temp+=q[i].c;
		else if(q[i].cnt>mid&&!q[i].opt) ans[q[i].ct]+=temp*q[i].c;
	int ll=l,rr=mid+1;
	for(int i=l;i<=r;i++) if(q[i].cnt<=mid) t[ll++]=q[i];else t[rr++]=q[i];
	for(int i=l;i<=r;i++) q[i]=t[i];
	cdq(l,mid),cdq(mid+1,r);
}

signed main()
{
	scanf("%lld%lld",&n,&m);
	for(int i=1;i<=n;i++) 
	{
		scanf("%lld",&a[i]);
		s[i]=s[i-1]+a[i];
	}
	for(int i=1;i<=m;i++)
	{
		int x;
		scanf("%lld",&x);
		if(x==1) ++cnt,scanf("%lld%lld",&q[cnt].x,&q[cnt].c),q[cnt].opt=1,q[cnt].cnt=cnt;
		else 
		{
			int l,r;
			scanf("%lld%lld",&l,&r);
			++ct,++cnt;
			q[cnt]={l-1,-1,0,cnt,ct};
			++cnt;
			q[cnt]={r,1,0,cnt,ct};
			ans[ct]=s[r]-s[l-1];
		}
	}
	sort(q+1,q+cnt+1,cmp);
	cdq(1,cnt);
	for(int i=1;i<=ct;i++) printf("%lld\n",ans[i]);
	
	return 0;
}

三维偏序

P3810 【模板】三维偏序(陌上花开)

这题的三个偏序关系已经给出,第一维 \(sort\),第二维 \(cdq\) ,第三维数据结构

点击查看代码
#include<bits/stdc++.h>
const int maxn=3e5+10;
using namespace std;
int n,k,cnt,c[maxn],dfn,vis[maxn],ans[maxn],temp[maxn];
struct lsx{int x,y,z,id,w;}q[maxn],t[maxn],a[maxn];

bool cmp(lsx a,lsx b)
{
	return a.x!=b.x?a.x<b.x:(a.y!=b.y?a.y<b.y:a.z<b.z);
}

bool check(lsx a,lsx b)
{
	if(a.x==b.x&&a.y==b.y&&a.z==b.z) return 1;
	else return 0;
}

int lowbit(int x){return x&-x;}

void add(int x,int y)
{
	while(x<=k)
	{
		if(vis[x]!=dfn) vis[x]=dfn,c[x]=0;
		c[x]+=y;
		x+=lowbit(x);
	}
}

int query(int x)
{
	int temp=0;
	while(x)
	{
		if(vis[x]==dfn)temp+=c[x];
		x-=lowbit(x);
	}
	return temp;
}

void cdq(int l,int r)
{
	if(l==r) return;
	int mid=l+r>>1;
	cdq(l,mid),cdq(mid+1,r);
	dfn++;
	int ll=l,rr=mid+1;
	for(int i=l;i<=r;i++)
	{
		if(ll<=mid&&q[ll].y<=q[rr].y||rr>r)
		{
			add(q[ll].z,q[ll].w);
			t[i]=q[ll++];
		}
		else
		{
			temp[q[rr].id]+=query(q[rr].z);
			t[i]=q[rr++];
		}
	}
	for(int i=l;i<=r;i++) q[i]=t[i];
}

int main()
{
	scanf("%d%d",&n,&k);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d%d",&a[i].x,&a[i].y,&a[i].z);
	}
	sort(a+1,a+n+1,cmp);
	int l=1;
	while(l<=n)
	{
		int r=l+1;
		while(r<=n&&check(a[l],a[r]))r++;
		q[++cnt]=a[l];
		q[cnt].w=r-l;
		q[cnt].id=cnt;
		l=r;
	}
	cdq(1,cnt);
	for(int i=1;i<=cnt;i++) ans[temp[q[i].id]+q[i].w-1]+=q[i].w;
	for(int i=0;i<n;i++) printf("%d\n",ans[i]);

	return 0;
}

三维偏序应用

P3157 [CQOI2011] 动态逆序对

这题考虑对一个坐标产生影响的操作是:时间更早,坐标考前,且比他大的值/时间更早,坐标考后,且比他小的值

所以这里的三个偏序关系是:时间,坐标,大小。

直接删肯定不好操作,我们反过来插入,考虑每次插入时新加的逆序对数有多少,权值树状数组维护即可。(要跑两遍)

点击查看代码
#include<bits/stdc++.h>
#define int long long 
const int maxn=1e5+10;
using namespace std;
int n,m,cnt,c[maxn],dfn,vis[maxn],ans[maxn],t,x[maxn],pre[maxn];
int in[maxn];
struct lsx{int t,pos,x,id;}q[maxn],a[maxn];

struct tree
{
	inline int lowbit(int x){return x&-x;}
	
	inline void add(int x,int y)
	{
		while(x<=n)
		{
			if(vis[x]!=dfn) vis[x]=dfn,c[x]=0;
			c[x]+=y;
			x+=lowbit(x);
		}
	}
	
	inline int query(int x)
	{
		int temp=0;
		while(x)
		{
			if(vis[x]==dfn)temp+=c[x];
			x-=lowbit(x);
		}
		return temp;
	}	
}e;

inline void cdq(int l,int r)
{
	if(l==r) return;
	int mid=l+r>>1;
	cdq(l,mid),cdq(mid+1,r);
	dfn++;
	int ll=l,rr=mid+1;
	for(int i=l;i<=r;i++)
	{
		if(ll<=mid&&q[ll].pos<q[rr].pos||rr>r)
		{
			e.add(q[ll].x,1);	
			ll++; 
		}
		else
		{
			ans[q[rr].id]+=e.query(n)-e.query(q[rr].x);
			rr++;
		}
	}
	dfn++;
	ll=mid,rr=r;
	for(int i=l;i<=r;i++)
	{
		if(ll>=l&&q[ll].pos>q[rr].pos||rr<=mid)
		{
			e.add(q[ll].x,1);	
			ll--; 
		}
		else
		{
			ans[q[rr].id]+=e.query(q[rr].x-1);
			rr--;
		}
	}
	ll=l;rr=mid+1;
    for (int i=l;i<=r;i++)
    {
        if ((ll<=mid&&q[ll].pos<q[rr].pos)||rr>r) a[i]=q[ll++];
        else a[i]=q[rr++];
    }
	for(int i=l;i<=r;i++) q[i]=a[i];
}

signed main()
{
//	freopen("P3157_1.in","r",stdin); 
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++)
	{
		cin>>x[i];
	}
	for(int i=1;i<=m;i++)
		cin>>in[i],pre[in[i]]=1;
	for(int i=1;i<=n;i++)
	{
		if(!pre[x[i]]) 
		{
			q[++cnt]={cnt,i,x[i],0};
		}
		else pre[x[i]]=i;
	}
	for(int i=m;i>=1;i--)
	{
		q[++cnt]={cnt,pre[in[i]],in[i],m-i+1};
	}
	cdq(1,cnt);
	for(int i=1;i<=m;i++) ans[i]+=ans[i-1];
    for(int i=m;i>=1;i--)
      cout<<ans[i]<<'\n';

	return 0;
}

P4169 [Violet] 天使玩偶/SJY摆棋子

求关于某个点曼哈顿距离(\(x,y\) 坐标)最近的点——\(dis(A,B) = |Ax-Bx|+|Ay-By∣\)

假设所有的点都在查询点的左下方,\(dis(A,B) = (Ax-Bx)+(Ay-By) = (Ax+Ay)-(Bx+By)\)

只要求满足\(Bx<Ax,By<Ay\) 且 \(Bx,By\) 之和最大的点就好了。

把整个平面翻转三次,进行四次\(cdq\) 分治,每次都只考虑左下的点即可

点击查看代码
#include<bits/stdc++.h>
const int maxn=1e6+1;
const int M=1e6+2;
using namespace std;
int n,m,cnt,c[M+8],dfn,vis[maxn],ans[maxn];
int in[maxn];
struct lsx{int x,y,id,xy,opt;}q[maxn],a[maxn],b[maxn];

struct tree
{
	inline int lowbit(int x){return x&-x;}
	
	inline void add(int x,int y)
	{
		while(x<=M)
		{
			if(vis[x]!=dfn) vis[x]=dfn,c[x]=1e9;
			c[x]=min(c[x],y);
			x+=lowbit(x);
		}
	}
	
	inline int query(int x)
	{
		int temp=1e9;
		while(x)
		{
			if(vis[x]==dfn)temp=min(c[x],temp);
			x-=lowbit(x);
		}
		return temp;
	}	
}e;

inline void cdq(int l,int r)
{
	if(l==r) return;
	int mid=l+r>>1;
	cdq(l,mid),cdq(mid+1,r);
	dfn++;
	int ll=l,rr=mid+1;
	for(int i=l;i<=r;i++)
	{
		if(ll<=mid&&q[ll].x<=q[rr].x||rr>r)
		{
			if(q[ll].opt==0)e.add(q[ll].y,-q[ll].xy);
			a[i]=q[ll];	
			ll++; 
		}
		else
		{
			if(q[rr].opt==1)ans[q[rr].id]=min(ans[q[rr].id],e.query(q[rr].y)+q[rr].xy);
			a[i]=q[rr];
			rr++;
		}
	}
	for(int i=l;i<=r;i++) q[i]=a[i];
}

int main()
{
//	freopen("P4169_10.in","r",stdin); 
//	freopen("ans.out","w",stdout); 
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	memset(ans,0x7f,sizeof ans);
	cin>>n>>m;
	int x,y,z;
	for(int i=1;i<=n;i++)
	{
		cin>>x>>y;x++,y++;
		b[i]={x,y,0,x+y,0};
	}
	for(int i=1;i<=m;i++)
	{
		cin>>z>>x>>y;x++,y++;
		b[i+n]={x,y,0,x+y,z-1};
		if(b[i+n].opt==1)
		{
			b[i+n].id=++cnt;
		}
	}
	
	for(int i=1;i<=n+m;i++) {
        q[i]=b[i];
    }
    cdq(1,n+m);
    
    for(int i=1;i<=n+m;i++) {
        q[i]=b[i];
        q[i].x=M-b[i].y;q[i].y=b[i].x;
        q[i].xy=q[i].x+q[i].y;
    }
    cdq(1,n+m);

    for(int i=1;i<=n+m;i++) {
        q[i]=b[i];
        q[i].x=M-b[i].x;q[i].y=M-b[i].y;
        q[i].xy=q[i].x+q[i].y;
    }
    cdq(1,n+m);

    for(int i=1;i<=n+m;i++) {
        q[i]=b[i];
        q[i].x=b[i].y;q[i].y=M-b[i].x;
        q[i].xy=q[i].x+q[i].y;
    }
    cdq(1,n+m);
    
    for(int i=1;i<=cnt;i++) 
	{
        cout<<ans[i]<<'\n';
    }
	
	return 0;
}

标签:总结,rr,int,ll,分治,++,maxn,cdq
From: https://www.cnblogs.com/oceansofstars/p/18351477

相关文章

  • MR开发恐龙项目总结
    在拥有权限的情况下读取安卓和windows的任意文件路径TArray<FString>ULoadGallery::GetPngFilesInOculusDirectory(){TArray<FString>FilesArray;IFileManager&FileManager=IFileManager::Get();FStringDirectoryPath;#ifPLATFORM_ANDROIDDi......
  • 十大java开发框架总结,微服务开发必备!
     提起java开发框架,大部分工程师可能主要使用的是ssh三件套,在当前微服务作为开发主流的时代,我们有必要也了解下其他java开发框架。1.SpringBoot SpringBoot是当前Java开发框架的首选,几乎是行业标准了。由轻量级Java开发框架spring进化而来。一直被模仿,从未被超越。2. Quar......
  • SpreadJS 个人学习及项目遇到的一些问题的总结
    最近公司有SpreadJS的部分,刚接触挺迷茫的,因为这个文档有点不清晰,有些属性啥的,看到跟没看一样,他没有那种效果图例说明,属性说的就很简单,看了大半天感觉没看出来啥,等开始做的时候就各种问题,感谢有同事替我们负重前行,趟过了很多的坑,这导致比预期入手好很多,目前只是算简单的上手,所以就......
  • C# & Unity 面向对象补全计划 七大原则 之 合成/聚合复用原则( CARP)难度:☆☆☆☆ 总结:
    本文仅作学习笔记与交流,不作任何商业用途,作者能力有限,如有不足还请斧正本系列作为七大原则和设计模式的进阶知识,看不懂没关系请看专栏:http://t.csdnimg.cn/mIitr,查漏补缺1.合成/聚合复用原则(CARP)        合成/聚合复用原则就是在一个新的对象里面使用一些已有的......
  • Hadoop学习总结
    在Hadoop学习的过程中,我进入了更具挑战性的阶段——编写和优化MapReduce任务。MapReduce是一种处理大规模数据集的编程模型,它将复杂的数据处理任务分解为两个主要阶段:Map(映射)和Reduce(归约)。通过这一过程,我不仅能解决实际的数据处理问题,还能在分布式环境中高效地执行计算任务。编......
  • 8.9第四周周五学习总结
    1最小生成树(讲课)【金山文档|WPS云文档】最小生成树https://kdocs.cn/l/cnDfoEEJS694prim模板(不常用)#include<bits/stdc++.h>usingnamespacestd;//#defineintlonglongconstintN=1100;constintmod=998244353;vector<int>v[N];#defineINF0x3f3f3f3f......
  • JavaScript -- 总结 10 (小白)
    MouseEvent属性<!DOCTYPEhtml><htmllang="en"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width,initial-scale=1.0"><title>Document<......
  • 【总结】注册码泄露原理以及例题
    引言题目给了小明的机器码:1653643685031597用户user_id:xiaoming可以看到题目采用了SIMD指令集该指令格式在CTF和攻防对抗中经常出现,可以提高执行效率的同时也可以增加逆向的难度。对于此类指令和题目,我们分析的方法是:遇到查意思,查的多了就跟看正常代码一样,采用动态分析。机......
  • Flux 生态更新总结
    :FLUXTinyVAE训练脚本FluxAIGridComparisons::FLUX生成的发型、服装、国籍、年龄等各种图像对比集合ComfyUI:适配 xlabsFluxcontrolnetcomfyui-replicateInstantX/FLUX.1-dev-Controlnet-Canny-alpha:又一个CannyControlNet模型daniel5984/flux_TrainingFLUX.1-DEV-Ca......
  • 深度学习每周学习总结N6:使用Word2vec实现文本分类
    ......