Gushing over Pr|

BigSmall_En

园龄:3年2个月粉丝:3关注:5

2021-12-20 20:21阅读: 77评论: 0推荐: 0

CDQ分治 的一些题目

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

https://www.luogu.com.cn/problem/P3810

本来就是很模板的题了,什么时候把题解补上,具体见代码

#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
using namespace std;
const int N=100005;
inline int read(){
	int x=0,f=1;char ch=getchar();
	while(ch<'0'||'9'<ch){if(ch=='-')f=-1;ch=getchar();}
	while('0'<=ch&&ch<='9'){x=(x<<3)+(x<<1)+(ch^48);ch=getchar();}
	return x*f;
}
struct node{int x,y,z,id,num,ans;}a[N],b[N];
inline bool cmpx(node x,node y){
	if(x.x==y.x)
		return x.y==y.y?x.z<y.z:x.y<y.y;
	return x.x<y.x;
}
inline bool cmpy(node x,node y){
	if(x.y==y.y)
		return x.z<y.z;
	return x.y<y.y;
}
int n,k,tot,bot[N],c[N];
inline int lowbit(int x){return x&-x;}
inline void update(int i,int v){for(;i<=k;i+=lowbit(i))c[i]+=v;}
inline int getsum(int i){int ans=0;for(;i;i-=lowbit(i))ans+=c[i];return ans;}
void CDQ(int l,int r){//按横坐标排好序之后再进行分治就只需要考虑y,z的情况了
	if(l==r)return;
	int mid=(l+r)>>1,i=l,j=mid+1;
	CDQ(l,mid);CDQ(mid+1,r);
	sort(a+l,a+mid+1,cmpy);
	sort(a+mid+1,a+r+1,cmpy);
	for(;j<=r;++j){//利用双指针,满足y的情况
		while(a[i].y<=a[j].y&&i<=mid)
			update(a[i].z,a[i].num),++i;
		a[j].ans+=getsum(a[j].z);//利用树状数组统计z满足的数的数量
	}
	for(int loc=l;loc<i;++loc)//只能清空对树状数组有共享的位置
		update(a[loc].z,-a[loc].num);
}//只需要考虑分治左边的数对右边的数的共享,分治右边的数自然也可以通过内部的分治来解决
int main(){
	n=read(),k=read();
	for(int i=1;i<=n;++i){
		b[i].x=read(),b[i].y=read(),b[i].z=read();
	}sort(b+1,b+1+n,cmpx);
	int tmp=0;
	for(int i=1;i<=n;++i){
		++tmp;//去重
		if(b[i].x!=b[i+1].x||b[i].y!=b[i+1].y||b[i].z!=b[i+1].z)
			a[++tot]=b[i],a[tot].num=tmp,tmp=0;
	}
	/*for(int i=1;i<=tot;++i)
		printf("%d %d %d %d\n",a[i].x,a[i].y,a[i].z,a[i].num);*/
	CDQ(1,tot);
	for(int i=1;i<=tot;++i)
		bot[a[i].ans+a[i].num-1]+=a[i].num;//相同的数可以认为满足偏序
	for(int i=0;i<n;++i)//显然只能到n-1
		printf("%d\n",bot[i]);
	return 0;
}// 613ms /  5.27MB /  1.54KB C++14 O2

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

https://www.luogu.com.cn/problem/P4169

需要见距离公式变形。如果要满足一个CDQ的性质,就需要将坐标轴旋转四个 \(90^{\circ}\) 求四次答案。

然后因为每次 cdq 完顺序会被打乱,如果重新按时间 \(O(n\log n)\) 排序,不如每次存入一个临时数组,然后 \(O(n)\) 直接复制过去。

#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int N=600005,R=10000007;//大概之前INF太大导致炸了
struct node{int x,y,opt,id,ans;}ori[N],a[N],tmp[N];
int n,m,rev;
struct BIT{
	int c[R];
	inline int lowbit(int x){return x&-x;}
	inline void update(int i,int v){for(;i<=rev;i+=lowbit(i))c[i]=max(c[i],v);}
	inline int getmax(int i,int ans=0){
		for(;i;i-=lowbit(i))ans=max(ans,c[i]);
		return ans?ans:-R;//特判为0,给-R是为了减去得到+R
	}
	inline void clear(int i){for(;c[i];i+=lowbit(i))c[i]=0;}
}t;
void cdq(int l,int r){
	if(l==r)return;
	int mid=(l+r)>>1,j=l,p1=l;
	cdq(l,mid);cdq(mid+1,r);
	for(int i=mid+1;i<=r;++i){
		while(j<=mid&&a[j].x<=a[i].x){
			if(a[j].opt==1)t.update(a[j].y,a[j].x+a[j].y);//详见距离计算公式
			tmp[p1++]=a[j++];
		}
		if(a[i].opt==2)
			ori[a[i].id].ans=min(ori[a[i].id].ans,a[i].x+a[i].y-t.getmax(a[i].y));
		tmp[p1++]=a[i];
	}
	for(int i=l;i<j;++i)
		if(a[i].opt==1)t.clear(a[i].y);
	while(j<=mid)tmp[p1++]=a[j++];//排好序,降低复杂度
	for(int i=l;i<=r;++i)a[i]=tmp[i];
}
inline void solve(int opx,int opy){//为统计答案,进行翻转
	for(int i=1;i<=n+m;++i){
		a[i]=ori[i];
		if(opx)a[i].x=rev-a[i].x;
		if(opy)a[i].y=rev-a[i].y;
	}cdq(1,n+m);
}
int main(){
	//freopen("P4169_10.in","r",stdin);
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i){
		int x,y;scanf("%d%d",&x,&y);
		ori[i].opt=1;ori[i].id=i;
		ori[i].x=++x,ori[i].y=++y;//这里放置坐标为0特殊处理。
		rev=max(rev,max(x,y));//确定翻转的轴线
	}
	for(int i=n+1;i<=n+m;++i){
		int opt,x,y;scanf("%d%d%d",&opt,&x,&y);
		ori[i].opt=opt,ori[i].id=i;
		ori[i].x=++x,ori[i].y=++y;ori[i].ans=R;
		rev=max(rev,max(x,y));
	}++rev;//这里防止重合也要特殊处理
	for(int i=0;i<=1;++i)
		for(int j=0;j<=1;++j)solve(i,j);
	for(int i=n+1;i<=n+m;++i)//答案是记录在ori中的
		if(ori[i].opt==2)printf("%d\n",ori[i].ans);
	return 0;
}

LG3157 [CQOI2011]动态逆序对

https://www.luogu.com.cn/problem/P3157

转化一下就是典型的三维偏序

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N=100005;
struct node{int opt,val,loc,id,tim;}opt[N<<1];
bool cmp(node x,node y){return x.loc<y.loc;}
int n,m,a[N],pos[N],tot,c[N];ll ans[N];
inline int lowbit(int x){return x&-x;}
inline void update(int i,int v){for(;i<=n;i+=lowbit(i))c[i]+=v;}
inline int getsum(int i){int ans=0;for(;i;i-=lowbit(i))ans+=c[i];return ans;}
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(opt+l,opt+mid+1,cmp);
	sort(opt+mid+1,opt+r+1,cmp);
	for(int i=mid+1;i<=r;++i){
		while(j<=mid&&opt[j].loc<=opt[i].loc)
			update(opt[j].val,opt[j].opt),++j;
		ans[opt[i].id]+=opt[i].opt*(getsum(n)-getsum(opt[i].val));
	}
	for(int i=l;i<j;++i)update(opt[i].val,-opt[i].opt);
	j=mid;
	for(int i=r;i>mid;--i){
		while(j>=l&&opt[j].loc>=opt[i].loc)
			update(opt[j].val,opt[j].opt),--j;
		ans[opt[i].id]+=opt[i].opt*getsum(opt[i].val-1);
	}
	for(int i=mid;i>j;--i)update(opt[i].val,-opt[i].opt);
}
int main(){
	scanf("%d%d",&n,&m);
	for(int i=1;i<=n;++i){
		scanf("%d",&a[i]);
		pos[a[i]]=i;
		opt[++tot]=(node){1,a[i],i,0,tot};
	}
	for(int i=1;i<=m;++i){
		int x;scanf("%d",&x);
		opt[++tot]=(node){-1,x,pos[x],i,tot};
	}
	cdq(1,tot);
	for(int i=1;i<=m;++i)ans[i]+=ans[i-1];
	for(int i=0;i<m;++i)printf("%lld\n",ans[i]);
	return 0;
}

本文作者:BigSmall_En

本文链接:https://www.cnblogs.com/BigSmall-En/p/15712762.html

版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。

posted @   BigSmall_En  阅读(77)  评论(0编辑  收藏  举报
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起