前言
最近学习了一下CDQ分治和整体分治,在这里做了一下整理,内容和代码有所借鉴,如果您有所不满,请通知我,我会删除。
CDQ分治
离线的做法,可以避免使用树套树这种数据结构。
通常要和树状数组一同使用。
流程
1. 按时间顺序处理出操作,并表好号。
2. 在CDQ分治中先处理出左右两个区间。
3. 然后处理出左区间对右区间的影响。
怎么理解呢? 因为右区间内的影响已经在子问题中处理出来了,所以只剩下左区间对右区间的影响还没有处理。
特别的需要注意CDQ分治只能处理离线(可能可以在线,但我不会)。
可以通过求逆序对(即二维偏序)来理解一下CDQ分治。
如果学过归并求逆序对的话,可以拿它来理解。
最最模板的是单点修改区间查询。
可能会说了:可以用树状数组秒过啊。
但是我们就是要搞搞事情用CDQ分治做一下子。
维护每一个的位置id最为第一权值,CDQ中维护id升序。
首先 可以把初始化当做修改可以方便处理
其次 要想要快速的查询出区间和的话,是可以差分的。
所以 将每一个查询拆分成两部分(a,b)a以内的减掉,b以内的加上
在CDQ中 记录左区间的修改量,处理右区间的查询。
代码
#include <iostream>
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn=500000+10;
struct node
{
int s,c,m,id;
bool operator < (const node &o)const
{
if(s!=o.s) return s<o.s;
else if(c!=o.c) return c<o.c;
else return m<o.m;
}
}a[maxn],tt[maxn],b[maxn],c[maxn];
int n,k,level[maxn],tree[maxn],ans[maxn];
int lowbit(int x)
{
return x&(-x);
}
void modify(int p,int d)
{
while(p<=k)
{
tree[p]+=d;
p+=lowbit(p);
}
}
int query(int p)
{
int ans=0;
while(p)
{
ans+=tree[p];
p-=lowbit(p);
}
return ans;
}
void cdq(int l,int r)
{
if(l>=r) return;
int mid=(l+r)>>1;
cdq(l,mid); cdq(mid+1,r);
int tot=0,p1=l,p2=mid+1;
for(int i=l; i<=r; i++)
{
if((p1<=mid && (a[p1].c<a[p2].c || (a[p1].c==a[p2].c && a[p1].m<=a[p2].m)))|| p2>r)
{
tot++; tt[tot]=a[p1]; b[i]=a[p1]; modify(a[p1].m,1); p1++; //左区间维护树状数组,并记录下来,方便清空
}
else
{
level[a[p2].id]+=query(a[p2].m); b[i]=a[p2]; p2++;
}
}
for(int i=1; i<=tot; i++)
{
modify(tt[i].m,-1);
}
for(int i=l; i<=r; i++)
{
a[i]=b[i];
}
}
int main()
{
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
scanf("%d %d",&n,&k);
for(int i=1; i<=n; i++)
{
scanf("%d %d %d",&a[i].s,&a[i].c,&a[i].m);
}
sort(a+1,a+n+1);
for(int i=1; i<=n; i++)
{
a[i].id=i;
c[i]=a[i];
}
cdq(1,n);
for(int i=n; i>=1; i--)
{
if(c[i].s==c[i+1].s && c[i].c==c[i+1].c && c[i].m==c[i+1].m)
level[i]=max(level[i],level[i+1]);
ans[level[i]]++;
}//因为有完全一样的,所以要这么处理,选出一样的当中最大的一个。
for(int i=0; i<n; i++)
{
printf("%d\n",ans[i]);
}
return 0;
}
整体二分
可以看一下这位dalao
说一下自己的理解吧。
整体二分就是所有的查询和修改一同处理,然后分类,哪一类的答案是….. 要做的就是分出这些类,并且可以记住一些信息降低常数。
带修改的区间第k大
[COGS 257] 动态排名系统
直接上代码
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
const int maxn=1000000;
struct node
{
int x,y,type,id,k,cur;
}a[maxn],c1[maxn],c2[maxn];
int tot,tree[maxn],totx,n,m,data[maxn];
int ans[maxn],type,tmp[maxn];
int T;
int lowbit(int x)
{
return x&(-x);
}
void modify(int x,int y)
{
while(x<=n)
{
tree[x]+=y;
x+=lowbit(x);
}
}
int query(int x)
{
int ans=0;
while(x)
{
ans+=tree[x];
x-=lowbit(x);
}
return ans;
}
void entire_divide(int L,int R,int l,int r)
{
if(L>R) return;
if(l>=r)
{
//对于L,R找到了答案l。
for(int i=L; i<=R; i++)
{
if(a[i].type==3) ans[a[i].id]=l;
}
return;
}
else
{
int mid=(l+r)>>1;
int l1=0,l2=0;
for(int i=L; i<=R; i++)
{
//处理处来小于mid的数有多少,记录在树状数组中。
if(a[i].type==1 && a[i].y<=mid) modify(a[i].x,1);//有这个点
else if(a[i].type==2 && a[i].y<=mid) modify(a[i].x,-1);//删除这个点
else if(a[i].type==3) tmp[i]=query(a[i].y)-query(a[i].x-1);//查分查询[x,y]中有多少小于mid的数。
}
//清空
for(int i=L; i<=R; i++)
{
if(a[i].type==1 && a[i].y<=mid) modify(a[i].x,-1);
else if(a[i].type==2 && a[i].y<=mid) modify(a[i].x,1);
}
//分类。
for(int i=L; i<=R; i++)
{
if(a[i].type==3)
{
if(a[i].cur+tmp[i]<a[i].k)//不够,分到右区间
{
l2++; a[i].cur+=tmp[i]; c2[l2]=a[i];
//累加,减少查询比mid小的数值。
}
else
c1[++l1]=a[i];//够了,分到左区间
}
else
{
if(a[i].y<=mid) c1[++l1]=a[i];//修改的比mid小
else c2[++l2]=a[i];//比mid大的放到右边。
}
}
for(int i=L; i<L+l1; i++)
{
a[i]=c1[i-L+1];
}
for(int i=L+l1; i<L+l1+l2; i++)
{
a[i]=c2[i-l1-L+1];
}
entire_divide(L,L+l1-1,l,mid); entire_divide(L+l1,R,mid+1,r);
//处理分好类的区间。
}
}
int main()
{
freopen("test.in","r",stdin);
freopen("test.out","w",stdout);
scanf("%d",&T);
while(T--)
{
memset(a,0,sizeof(a));
scanf("%d %d",&n,&m);
tot=0,totx=0;
for(int i=1; i<=n; i++)
{
scanf("%d",&data[i]);
tot++; a[tot]=(node){i,data[i],1,0,0,0};//将初始数据当做修改
}
for(int i=1; i<=m; i++)
{
char ch=getchar();
while(ch!='Q' && ch!='C') ch=getchar();
if(ch=='Q')
{
int x,y,w; scanf("%d %d %d",&x,&y,&w);
tot++; totx++;
a[tot]=(node){x,y,3,totx,w,0};
}
else
{
int x,y; scanf("%d %d",&x,&y);
//修改分为两部分,2是删除,1是插入。
tot++; a[tot]=(node){x,data[x],2,0,0,0};
tot++; a[tot]=(node){x,y,1,0,0,0};
data[x]=y;
}
}
entire_divide(1,tot,0,1000000000);
for(int i=1; i<=totx; i++)
{
printf("%d\n",ans[i]);
}
}
return 0;
}
尾言
欢迎dalao指教。