cdq分治(new)

cdp分治

思想:分治

用途:

tips1:解决点对有关问题

【mokia】给出一个矩阵,支持2种操作,(1)add(x,y,val)把(x,y)单点增加val(2)query(x1,y1,x2,y2)查询矩形区间内的和值

三维偏序,将模型从问题中抽离,在线-->离线

初始矩阵为0,贡献只来自于add。把询问拆成4部分,然后考虑对query有贡献的add,\(x<=x2,y<=y2\),而且时间必须在query之前,就是裸三维偏序

点击查看代码
#include<bits/stdc++.h>
using namespace std;
#define lowbit(x) (x&(-x))  
int low[2000000+10],w;
struct node
{
	int x,y,tim,val,opt;
}q[200000+100];int tot,timd;
inline bool cmp1(node ar,node br)
{
	return ar.tim<br.tim;
}
inline bool cmp2(node ar,node br)
{
	return (ar.x==br.x)?(ar.y<br.y):(ar.x<br.x);
}
inline void add(int x,int vl)
{
	while(x<=w)
	{
		low[x]+=vl;x+=lowbit(x);
	}
}
inline int query(int x)
{
	int ans=0;
	while(x)
	{
		ans+=low[x];x-=lowbit(x);
	}
	return ans;
}
inline void CDQ(int l,int r)
{
	if(l==r)return;
	int mid=(l+r)>>1;
	CDQ(l,mid);CDQ(mid+1,r);
//	printf("qeury:%d %d\n",l,r);
	sort(q+l,q+mid+1,cmp2);sort(q+mid+1,q+r+1,cmp2);
	int posl=l;
	for(int posr=mid+1;posr<=r;++posr)
	{
		while(posl<=mid&&q[posl].x<=q[posr].x)
		{
			if(q[posl].opt==0)add(q[posl].y,q[posl].val);
			posl++;
		}
		if(q[posr].opt==1)q[posr].val+=query(q[posr].y);//,printf("que:%d\n",q[posr].val);
	}
	for(int i=l;i<=posl-1;++i)
	if(q[i].opt==0)add(q[i].y,-q[i].val);
}
int main()
{
	int ope;scanf("%d%d",&ope,&w);++w;
	while(1)
	{
		scanf("%d",&ope);
		if(ope==1)
		{
			int xr,yr,var;
			scanf("%d%d%d",&xr,&yr,&var);xr++;yr++;
			q[++tot]=(node){xr,yr,++timd,var,0};
		}
		else if(ope==2)
		{
			int xr,yr,xd,yd;
			scanf("%d%d%d%d",&xr,&yr,&xd,&yd);xd++;yd++;
			q[++tot]=(node){xr,yr,++timd,0,1};//++--
			q[++tot]=(node){xd,yd,++timd,0,1};
			q[++tot]=(node){xr,yd,++timd,0,1};
			q[++tot]=(node){xd,yr,++timd,0,1};
		//	printf("(qwuery)%d %d %d %d\n",timd-3,timd-2,timd-1,timd);
		}
		else break;
	}
	CDQ(1,tot);
	sort(q+1,q+1+tot,cmp1);
	for(int i=1;i<=tot;)
	{
		if(q[i].opt==1)
		{
			int ans=0;
		//	printf("(%d %d %d %d)%d %d %d %d\n",i,i+1,i+2,i+3,q[i].val,q[i+1].val,q[i+2].val,q[i+3].val);
			ans=q[i].val+q[i+1].val-q[i+2].val-q[i+3].val;
			i=i+4;
			printf("%d\n",ans);
		}
		else ++i;
	}
	 return 0;
}
/*
0 4
1 2 3 3
2 1 1 3 3
1 2 2 2
2 2 2 3 4
3

3
5
*/

【天使玩偶】题意:m次操作,(1)(l,r)求距离(l,r)最近的点到(l,r)距离(2)(l,r)添加平面内的点(l,r)m<=5e5,l,r<=1e6

首先对于询问(1):
把平面分成4部分,对于左下部分(x+y)-max(xi+yi),就可以直接转换成三维偏序,
对于其他3个部分,直接坐标轴相对位置翻转x=max_x-x就行。
对于加入,时间的一维偏序而已
第一维时间:添加操作顺序本身维护
第二维x:归并排序
第三维y:树状数组

点击查看代码


#include<bits/stdc++.h>
using namespace std;
#define rint register int
#define _f(i,a,b) for(rint i=a;i<=b;++i)
#define f_(i,a,b) for(rint i=a;i>=b;--i)
#define chu printf
#define ll long long
inline ll re()
{
	ll h=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')h=-1;ch=getchar();}
	while(ch<='9'&&ch>='0'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*h;
}
const int N=5e5,M=1e6;
struct Node
{
	int id,x,y,ans,num;
}e[(N<<1)+100],g[(N<<1)+100],tmp[(N<<1)+100];
int tot,n,m,Mx=1e7,Mxlen;
int low[M+100];//维护y
#define lowbit(x) (x&(-x))
inline void Insert(int x,int val)
{
	while(x<=Mxlen)
	{
		low[x]=max(low[x],val);
		x+=lowbit(x);
	}
}
inline int Query(int x)
{
	int nas=0;
	while(x)
	{
		nas=max(nas,low[x]);
		x-=lowbit(x);
	}
	if(!nas)return -Mx;
	return nas;
}
inline void Clear(int x)
{
	while(x<=Mxlen)
	{
		low[x]=0;
		x+=lowbit(x);
	}
}
inline void cdq(int l,int r)
{
	if(l==r)return;
	int mid=(l+r)>>1;
	cdq(l,mid);cdq(mid+1,r);
	int lq=l,ins=l;
	_f(rq,mid+1,r)
	{
		while(g[lq].x<=g[rq].x&&lq<=mid)
		{
			if(g[lq].id==1)Insert(g[lq].y,g[lq].x+g[lq].y);
			tmp[ins]=g[lq];++ins;++lq;
		}
		if(g[rq].id==2)e[g[rq].num].ans=min(e[g[rq].num].ans,g[rq].x+g[rq].y-Query(g[rq].y));
		tmp[ins]=g[rq];++ins;
	}
	_f(i,l,lq-1)
	if(g[i].id==1)Clear(g[i].y);
	while(lq<=mid)
	tmp[ins]=g[lq],++ins,++lq;
	_f(i,l,r)g[i]=tmp[i];
}
void solve(int opt1,int opt2)
{
	_f(i,1,tot)
	{
		g[i]=e[i];
		if(opt1)g[i].x=Mxlen-g[i].x;
		if(opt2)g[i].y=Mxlen-g[i].y;
	}
	cdq(1,tot);
}
int main()
{
	//freopen("a.in","r",stdin);
	//freopen("a.out","w",stdout);
	n=re(),m=re();
	_f(i,1,n)
	{
		e[i].x=re()+1,e[i].y=re()+1,e[i].id=1;Mxlen=max(e[i].y,max(e[i].x,Mxlen));
		e[i].num=i;
	}
	_f(i,n+1,m+n)
	{
		e[i].id=re();e[i].x=re()+1,e[i].y=re()+1;Mxlen=max(e[i].y,max(e[i].x,Mxlen));
		e[i].num=i;
		if(e[i].id==2)e[i].ans=M;
	}
	tot=n+m;Mxlen++;
	solve(0,0);solve(0,1);
	solve(1,0);solve(1,1);
	_f(i,n+1,n+m)
	if(e[i].id==2)chu("%d\n",e[i].ans);
	return 0;
}
/*
题意:m次操作,
(1)(l,r)求距离(l,r)最近的点到(l,r)距离
(2)(l,r)添加平面内的点(l,r)
m<=5e5,l,r<=1e6

首先对于询问(1):
把平面分成4部分,对于左下部分(x+y)-max(xi+yi),就可以直接转换成三维偏序,
对于其他3个部分,直接坐标轴相对位置翻转x=max_x-x就行。
对于加入,时间的一维偏序而已
第一维时间:添加操作顺序本身维护
第二维x:归并排序
第三维y:树状数组
*/

【动态逆序对】动态逆序对

我们考虑求出每个位置的值的贡献,就是“pos位置的值和删除时间在pos后面的数所构成的逆序对的对数”,分为2个部分,就是
(1)posj<posi,valj>vali
(2)posj>posi,valj<vali
他们都要满足:timj>timi,三维偏序,很显然了吧。然后求出dev[pos],倒着删除每个数的贡献就可以。注意必须求的是timj>timi的,不能是timj<timi的,
不然对于整体个数是对的,但是没办法删除把这个数删除的所有贡献。

点击查看代码








#include<bits/stdc++.h>
using namespace std;
#define rint register int
#define _f(i,a,b) for(rint i=a;i<=b;++i)
#define f_(i,a,b) for(rint i=a;i>=b;--i)
#define chu printf
#define ll long long
inline ll re()
{
	ll h=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')h=-1;ch=getchar();}
	while(ch<='9'&&ch>='0'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*h;
}
#define int ll
const int N=1e5,M=5e4;
struct Node
{
	int val,tim,ans;//值,还有被删除的时间
	bool operator<(const Node&A)const
	{
		return tim<A.tim;
	}
}a[N+100],tmp[N+100];
int n,m,res[N+10];
int low[N+100];
struct Code
{
	int val,pos;
	bool operator<(const Code&A)const
	{
		return val<A.val;
	}
}b[N+100];
#define lowbit(x)  (x&(-x))
inline void Add(int x,int val)
{
	while(x<=n)
	{
		low[x]+=val;x+=lowbit(x);
	}
}
inline int Query(int x)
{
	int ans=0;
	while(x)
	{
		ans+=low[x];x-=lowbit(x);
	}
	return ans;
}
/*
L.pos<R.pos(这个已经保证了)
对左边统计右边
L.opt<=R.opt(右边加入贡献的必须没有被添加):merge_sort
L.val>R.val(树状数组?)
*/
inline void cdq(int l,int r)
{
	if(l==r)return ;
	
	int mid=(l+r)>>1;
	cdq(l,mid);cdq(mid+1,r);
	int rq1=r;
	f_(lq1,mid,l)
	{
		while(a[lq1].tim<a[rq1].tim&&rq1>=mid+1)
		{
			Add(a[rq1].val,1);--rq1;
		}
		res[a[lq1].tim]+=Query(a[lq1].val);
	}
	_f(i,rq1+1,r)
	Add(a[i].val,-1);
	int lq2=mid;
	f_(rq2,r,mid+1)
	{
		while(a[rq2].tim<a[lq2].tim&&lq2>=l)
		{
			Add(a[lq2].val,1);--lq2;
		}
		res[a[rq2].tim]+=Query(n)-Query(a[rq2].val);
	}
	_f(i,lq2+1,mid)Add(a[i].val,-1);
	int ins=l;int lqq=l;
	_f(rqq,mid+1,r)
	{
		while(a[lqq].tim<a[rqq].tim&&lqq<=mid)
		{
		//	chu("in\n");
			tmp[ins]=a[lqq];++lqq;++ins;
		}
		//chu("insdert:%d\n",a[rqq].val);
		tmp[ins]=a[rqq];++ins;
	}
//	chu("ins:%dlqq:%d(%d)\n",ins,lqq,a[lqq].val);
	while(lqq<=mid)
	{
		tmp[ins]=a[lqq];++ins;++lqq;
	}
	_f(i,l,r)a[i]=tmp[i];	
}
signed main()
{
	//freopen("asn.txt","r",stdin);
	n=re();m=re();
	_f(i,1,n)
	{
		a[i].val=re();
		b[a[i].val].pos=i;
	}
	_f(i,1,m)
	{
		int x=re();
		int ps=b[x].pos;
		a[ps].tim=i;
		//chu("num:%d  pos:%d\n",x,ps);
	}
	int pd=m;
	_f(i,1,n)
	if(!a[i].tim)a[i].tim=++pd;
	//_f(i,1,n)chu("a[%d].tim:%d(%d)\n",i,a[i].tim,a[i].qr);
	cdq(1,n);
//	_f(i,1,n)chu("I:%d  ans:%d\n",i,a[i].ans);
	int ero=0;
	f_(i,n,1)ero=ero+res[i];
	_f(i,1,m)
	chu("%lld\n",ero),ero-=res[i];
	return 0;
}
/*

*/

【序列】动态最长上升

dp[i]=max(dp[j]+1,dp[i])
(mx[j]<=val[i],val[j]<=mi[i])
直接转移n<=1e5,O(n^2)会T,考虑cdq分治处理偏序关系:
因为偏序关系的依据不固定,所以归并就不好解决,这时候可以用sort(),cmp函数里定义第一维度优先级是x,第二维度是id,id代表位置,
(l,r)里面用(l,mid)位置更新(mid+1,r)位置,(x,y)赋值相对的mx,mi,val就可以了。
第二维度的偏序就是x,sort解决了(遍历顺序)
第三维度就是树状数组维护了。

点击查看代码


#include<iostream>
#include<cstdio>
#include<cmath>
#include<ctime>
#include<cstring>
#include<cstdlib>
#include<iomanip>
#include<algorithm>
#include<vector>
#include<deque>
#include<queue>
#include<map>
#include<set>
using namespace std;
#define rint register int
#define _f(i,a,b) for(rint i=a;i<=b;++i)
#define f_(i,a,b) for(rint i=a;i>=b;--i)
#define chu printf
#define ll long long
#define INF 2147483647
inline ll re()
{
	ll h=1,x=0;char ch=getchar();
	while(ch<'0'||ch>'9'){if(ch=='-')h=-1;ch=getchar();}
	while(ch<='9'&&ch>='0'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	return x*h;
}
const ll mod=998244353;
int n,m;
struct node
{
	int mx,mi,val;
}e[100000+10];
struct code
{
	int x,y,id;
}tmp[100000+10];
int mxb;
int dp[100000+10],low[100000+10];
bool cmp(code& A,code& B)
{
	if(A.x==B.x)return A.id<B.id;
	return A.x<B.x;
}
inline void goin(int x,int val)
{
	//chu("addval(%d):%d\n",x,val);
	while(x<=mxb)
	{
		low[x]=max(low[x],val);
		x+=(x&(-x));
	}
}
inline int query(int x)
{
	int ans=0;
//	chu("x:%d\n",x);
	while(x)
	{
		ans=max(ans,low[x]);
		x-=(x&(-x));
	}
	//dp没有值就是没找到转移
	//chu("return:%d\n",ans);
	return ans;
}
inline void goout(int x)
{
	while(x<=mxb)
	{
		low[x]=0;
		x+=(x&(-x));
	}
}
inline void cdq(int l,int r)
{
	if(l==r)
	{
		dp[l]=max(dp[l],1);return;
	}
	//chu("%d--%d\n",l,r);
	int mid=(l+r)>>1;
	cdq(l,mid);
	_f(i,l,r)
	{
		//chu("e:%d %d %d\n",e[i].val,e[i].mi,e[i].mx);
		if(i<=mid)tmp[i].x=e[i].val,tmp[i].y=e[i].mx;
		else tmp[i].x=e[i].mi,tmp[i].y=e[i].val;
		tmp[i].id=i;
	}
	sort(tmp+l,tmp+r+1,cmp);
	_f(i,l,r)
	{
		//chu("tmp:%d %d %d\n",tmp[i].id,tmp[i].x,tmp[i].y);
		if(tmp[i].id<=mid)goin(tmp[i].y,dp[tmp[i].id]);
		else dp[tmp[i].id]=max(query(tmp[i].y)+1,dp[tmp[i].id]);
	}
	_f(i,l,r)
	{
		if(tmp[i].id<=mid)goout(tmp[i].y);
	}
	cdq(mid+1,r);
}
int main()
{
	n=re(),m=re();
	_f(i,1,n)e[i].val=e[i].mx=e[i].mi=re(),mxb=max(e[i].mx,mxb);
	_f(i,1,m)
	{
		int x=re(),y=re();
		e[x].mx=max(e[x].mx,y);
		e[x].mi=min(e[x].mi,y);
		mxb=max(mxb,y);
	}
	cdq(1,n);
	int ans=0;
	_f(i,1,n)ans=max(ans,dp[i]);
	chu("%d",ans);
	return 0;
}
/*

*/
posted on 2022-08-25 17:05  HZOI-曹蓉  阅读(21)  评论(0编辑  收藏  举报