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;
}
/*
*/