那是一波强行计算,  下边是一波瞎往往威尼斯人娱乐

前言

  这是一波强行总计。

  上边是一波瞎反复。

  这几天做了几道CDQ/全体二分,感觉温馨做题速度好慢啊。

  很多很肯定的事物都看不出来
分治分不出去 打不出去 调不对

  早上午后中午的频率完全不等同啊。

  完蛋.jpg 绝望.jpg。

 

前言

  这是一波粗犷总括。

  上面是一波瞎再三。

  这几天做了几道CDQ/全部二分,感觉自己做题速度好慢啊。

  很多很强烈的事物都看不出来
分治分不出去 打不出去 调不对

  早上上午晚间的功用完全差距啊。

  完蛋.jpg 绝望.jpg。

 

关于CDQ分治

  CDQ分治,求的是三维偏序问题都清楚的。

  求法呢,就是在分治外面先把一维变成有序

  然后分治下去,左边(l,mid)关于左边(mid+1,r)就不存在某一维的逆序了,所以唯有两维偏序了。

  那么些时候来一波”树状数组求逆序对”的操作搞一下二维偏序

  就可以把跨过中线的,左侧更新右侧的情形总括出来。

  只顾:只统计左边的操作对左侧的询问的进献!

  然后左右两边递归处理就好了。

  正确性:依照线段树的形态递归的CDQ分治,保险每一对三元组在线段树上都有且仅有一个LCA(那不废话吗),而这一组答案就会且仅会在LCA处总结。假如在LCA上面,点对不在一个work内自然不会盘算。如若在LCA上边了,点对就在同一侧,不会相互更新。

  复杂度:设一回work的复杂度是f(len),则复杂度是O(f(n)logn)。

  一般都在分治里用树状数组,一般的复杂度就是O(nlog2n)的。

  一般是那样的套路:借使三维偏序分别为a,b,c;

  在main函数里有限支撑a递增。

  然后在CDQ里先分治左右,传下去的时候a依旧递增,不损坏性质。

  然后分治完左右两边后,需确保左右两边分别b都是星罗棋布的(a不重大)。

  然后就是近乎归并排序的操作了。

  此时左手的a肯定都自愧不如左边的a,那么一旦对于一个右手的要素

  以前好像归并的操作就足以确保拥有小于b的左手的要素都已经遍历过。

  那么找c也低于它的?值域线段树/树状数组等数据结构维护一下就好了。

  然后您如此归并了一波后,就发现总计完答案后b是有序递增的了(那一个时候a已经不紧要了)。

  对于上层操作,符合”左右两边分别b是雨后春笋的”了。

  BZOJ陌上花开竟然是权力题?那是在搞笑。

  好吧BZOJ动态逆序对,此前写过的,做四回CDQ就好了。

  BZOJ稻草人,也是CDQ。

 

还有一个就是高维偏序问题。

cogs上的2479 HZOI2016 偏序
就是四维偏序板子。

后边还有多个加强版,到了七维,不是CDQ干的作业,详情请见这个PPT

那边只谈谈四维偏序,即a<a’   b<b’  
c<c’   d<d’。

做法是可爱的CDQ套CDQ套树状数组。

有个很妙的博客:Candy?

首先在外围根据a排好序。

进第一层CDQ。先递归处理,然后标记本来是在mid左侧依然右侧的,左1右0,然后按b排序。

要么只总结左侧部分跨过中线对左侧部分的进献。

依据b排好序后,就改为了计算标记为0的点的”在它左侧的、标记为1的、(c,d)都自愧不如它的点的个数”。

“在它左边+(c,d)都低于它” =
三维偏序。

复制到另一个数组里再做五遍cdq就足以了。

复杂度O(nlog^3n)。

 

威尼斯人娱乐 1威尼斯人娱乐 2

#include    <iostream>
#include    <cstdio>
#include    <cstdlib>
#include    <algorithm>
#include    <vector>
#include    <cstring>
#include    <queue>
#include    <complex>
#include    <stack>
#define LL long long int
#define dob double
#define FILE "partial_order"
//#define FILE "CDQ"
using namespace std;

const int N = 100010;
struct Data{int a,b,c,id;}p[N],que[N],que2[N];
int n,vis[N],tim,T[N];
LL Ans;

inline int gi(){
  int x=0,res=1;char ch=getchar();
  while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();}
  while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
  return x*res;
}

inline void update(int x){
  for(;x<=n;x+=x&-x){
    if(vis[x]!=tim)T[x]=0,vis[x]=tim;
    T[x]++;
  }
}

inline int query(int x,int ans=0){
  for(;x;x-=x&-x){
    if(vis[x]!=tim)T[x]=0,vis[x]=tim;
    ans+=T[x];
  }
  return ans;
}

inline void cdq(int l,int r){
  if(l==r)return;
  int mid=(l+r)>>1,i=l,j=mid+1,k=l;
  cdq(l,mid);cdq(mid+1,r);tim++;
  while(i<=mid && j<=r){
    if(que[i].b<que[j].b){
      if(que[i].id)update(que[i].c);
      que2[k++]=que[i++];
    }
    else{
      if(!que[j].id)Ans+=query(que[j].c);
      que2[k++]=que[j++];
    }
  }
  while(i<=mid)que2[k++]=que[i++];
  while(j<=r){
    if(!que[j].id)Ans+=query(que[j].c);
    que2[k++]=que[j++];
  }
  for(k=l;k<=r;++k)que[k]=que2[k];
}

inline void CDQ(int l,int r){
  if(l==r)return;
  int mid=(l+r)>>1,i=l,j=mid+1,k=l;
  CDQ(l,mid);CDQ(mid+1,r);
  while(i<=mid && j<=r){
    if(p[i].a<p[j].a)que[k]=p[i++],que[k++].id=1;
    else que[k]=p[j++],que[k++].id=0;
  }
  while(i<=mid)que[k]=p[i++],que[k++].id=1;
  while(j<=r)que[k]=p[j++],que[k++].id=0;
  for(k=l;k<=r;++k)p[k]=que[k];cdq(l,r);
}

int main()
{
  freopen(FILE".in","r",stdin);
  freopen(FILE".out","w",stdout);
  n=gi();
  for(int i=1;i<=n;++i)p[i].a=gi();
  for(int i=1;i<=n;++i)p[i].b=gi();
  for(int i=1;i<=n;++i)p[i].c=gi();
  CDQ(1,n);printf("%lld\n",Ans);
  fclose(stdin);fclose(stdout);
  return 0;
}

CDQ套CDQ

 

 

 

 

关于CDQ分治

  CDQ分治,求的是三维偏序问题都清楚的。

  求法呢,就是在分治外面先把一维变成有序

  然后分治下去,右侧(l,mid)关于左侧(mid+1,r)就不设有某一维的逆序了,所以只有两维偏序了。

  那些时候来一波”树状数组求逆序对”的操作搞一下二维偏序

  就能够把跨过中线的,左侧更新左侧的图景总括出来。

  只顾:只统计左侧的操作对左边的刺探的孝敬!

  然后左右两边递归处理就好了。

  正确性:根据线段树的形象递归的CDQ分治,保险每一对长富组在线段树上都有且仅有一个LCA(那不废话吗),而这一组答案就会且仅会在LCA处总计。如若在LCA下边,点对不在一个work内自然不会计算。尽管在LCA下边了,点对就在同一侧,不会相互更新。

  复杂度:设四遍work的复杂度是f(len),则复杂度是O(f(n)logn)。

  一般都在分治里用树状数组,一般的复杂度就是O(nlog2n)的。

  一般是那样的套路:假诺三维偏序分别为a,b,c;

  在main函数里保障a递增。

  然后在CDQ里先分治左右,传下去的时候a依旧递增,不损坏性质。

  然后分治完左右两边后,需确保左右两边分别b都是星罗棋布的(a不重大)。

  然后就是接近归并排序的操作了。

  此时左手的a肯定都自愧不如左侧的a,那么一旦对于一个右手的因素

  此前好像归并的操作就足以确保拥有小于b的左手的要素都已经遍历过。

  那么找c也低于它的?值域线段树/树状数组等数据结构维护一下就好了。

  然后您那样归并了一波后,就发现计算完答案后b是逐步递增的了(这些时候a已经不重大了)。

  对于上层操作,符合”左右两边分别b是比比皆是的”了。

  BZOJ陌上花开竟然是权力题?那是在搞笑。

  好吧BZOJ动态逆序对,以前写过的,做四回CDQ就好了。

  BZOJ稻草人,也是CDQ。

 

再有一个就是高维偏序问题。

cogs上的2479 HZOI2016 偏序
就是四维偏序板子。

末端还有五个加强版,到了七维,不是CDQ干的事务,详情请见这个PPT

此地只谈谈四维偏序,即a<a’   b<b’  
c<c’   d<d’。

做法是可爱的CDQ套CDQ套树状数组。

有个很妙的博客:Candy?

先是在外围依据a排好序。

进第一层CDQ。先递归处理,然后标记本来是在mid左侧仍然右手的,左1右0,然后按b排序。

如故只计算左侧部分跨过中线对左侧部分的进献。

按部就班b排好序后,就改为了总括标记为0的点的”在它右侧的、标记为1的、(c,d)都自愧不如它的点的个数”。

“在它右边+(c,d)都低于它” =
三维偏序。

复制到另一个数组里再做三回cdq就足以了。

复杂度O(nlog^3n)。

 

威尼斯人娱乐 3威尼斯人娱乐 4

#include    <iostream>
#include    <cstdio>
#include    <cstdlib>
#include    <algorithm>
#include    <vector>
#include    <cstring>
#include    <queue>
#include    <complex>
#include    <stack>
#define LL long long int
#define dob double
#define FILE "partial_order"
//#define FILE "CDQ"
using namespace std;

const int N = 100010;
struct Data{int a,b,c,id;}p[N],que[N],que2[N];
int n,vis[N],tim,T[N];
LL Ans;

inline int gi(){
  int x=0,res=1;char ch=getchar();
  while(ch>'9'||ch<'0'){if(ch=='-')res*=-1;ch=getchar();}
  while(ch<='9'&&ch>='0')x=x*10+ch-48,ch=getchar();
  return x*res;
}

inline void update(int x){
  for(;x<=n;x+=x&-x){
    if(vis[x]!=tim)T[x]=0,vis[x]=tim;
    T[x]++;
  }
}

inline int query(int x,int ans=0){
  for(;x;x-=x&-x){
    if(vis[x]!=tim)T[x]=0,vis[x]=tim;
    ans+=T[x];
  }
  return ans;
}

inline void cdq(int l,int r){
  if(l==r)return;
  int mid=(l+r)>>1,i=l,j=mid+1,k=l;
  cdq(l,mid);cdq(mid+1,r);tim++;
  while(i<=mid && j<=r){
    if(que[i].b<que[j].b){
      if(que[i].id)update(que[i].c);
      que2[k++]=que[i++];
    }
    else{
      if(!que[j].id)Ans+=query(que[j].c);
      que2[k++]=que[j++];
    }
  }
  while(i<=mid)que2[k++]=que[i++];
  while(j<=r){
    if(!que[j].id)Ans+=query(que[j].c);
    que2[k++]=que[j++];
  }
  for(k=l;k<=r;++k)que[k]=que2[k];
}

inline void CDQ(int l,int r){
  if(l==r)return;
  int mid=(l+r)>>1,i=l,j=mid+1,k=l;
  CDQ(l,mid);CDQ(mid+1,r);
  while(i<=mid && j<=r){
    if(p[i].a<p[j].a)que[k]=p[i++],que[k++].id=1;
    else que[k]=p[j++],que[k++].id=0;
  }
  while(i<=mid)que[k]=p[i++],que[k++].id=1;
  while(j<=r)que[k]=p[j++],que[k++].id=0;
  for(k=l;k<=r;++k)p[k]=que[k];cdq(l,r);
}

int main()
{
  freopen(FILE".in","r",stdin);
  freopen(FILE".out","w",stdout);
  n=gi();
  for(int i=1;i<=n;++i)p[i].a=gi();
  for(int i=1;i<=n;++i)p[i].b=gi();
  for(int i=1;i<=n;++i)p[i].c=gi();
  CDQ(1,n);printf("%lld\n",Ans);
  fclose(stdin);fclose(stdout);
  return 0;
}

CDQ套CDQ

 

 

 

 

有关全体二分

  全体二分重倘诺把所有询问放在一块儿二分答案,然后把操作也一块儿分治。

  何时用吗?

  当你发现多组了解可以离线的时候

  当您发现询问可以二分答案而且check复杂度对于单组询问可以承受的时候

  当你发觉询问的操作都是同等的的时候

  你就足以应用完全二分那些事物了。

  具体做法讲起来有些玄学,其实类似主席树转化到距离的操作仍然线段树上二分。

  想想:二分答案的时候,对于一个答案,是或不是稍稍操作是没用的,有些操作贡献是不变的?

  比如二分一个年华,那么时间前边暴发的操作就是从未用的,时间前边的孝敬是不变的。

  二分一个最大值,比mid大的都是没用的,比mid小的个数是一定的。

  全部二分就是应用了如此一个性能。

  二分答案,然后把尚未用的操作扫进右侧,和答案在[mid+1,r]的打听一起递归处理。

  把实用的操作放进左侧,减去不变的贡献,和答案在[l,mid]的一道递归处理。

  只顾答案在[mid+1,r]的刺探要算上放进了左边的操作的孝敬,开个变量记下来/间接压缩都得以。

  专注全部二分在solve内的复杂度一定只可以与区间长度线性相关,无法每一次都有其他复杂度!

  比如一次solve的复杂度是O(lenlogn)就足以,O(len+sqrt(n))就不行。

  大致就是那样一个东西。

  复杂度?和CDQ是平等的,都是O(f(len)logn)。

  例题?BZOJ3110
K大数查询 Codevs Meteors。

  一样的老路了。

 

至于全体二分

  全部二分重点是把具有询问放在一块儿二分答案,然后把操作也一并分治。

  什么日期用呢?

  当您意识多组明白可以离线的时候

  当你意识询问可以二分答案而且check复杂度对于单组询问可以承受的时候

  当您发觉询问的操作都是同等的的时候

  你就可以行使完全二分这一个东西了。

  具体做法讲起来有点玄学,其实看似主席树转化到距离的操作依旧线段树上二分。

  想想:二分答案的时候,对于一个答案,是否稍稍操作是没用的,有些操作进献是不变的?

  比如二分一个小时,那么时间前边发生的操作就是没有用的,时间前边的进献是不变的。

  二分一个最大值,比mid大的都是没用的,比mid小的个数是早晚的。

  全部二分就是使用了这么一个特性。

  二分答案,然后把没有用的操作扫进左边,和答案在[mid+1,r]的问询一起递归处理。

  把有效的操作放进左边,减去不变的贡献,和答案在[l,mid]的联合递归处理。

  在意答案在[mid+1,r]的打听要算上放进了左手的操作的贡献,开个变量记下来/直接压缩都可以。

  留意全体二分在solve内的复杂度一定只好与区间长度线性相关,不可以每一次都有其他复杂度!

  比如两次solve的复杂度是O(lenlogn)就足以,O(len+sqrt(n))就万分。

  大致就是如此一个事物。

  复杂度?和CDQ是一律的,都是O(f(len)logn)。

  例题?BZOJ3110
K大数查询 Codevs Meteors。

  一样的套路了。

 

有关部分要注意的地点

  归并自然要把剩余的搞完!每回自我都遗忘那码子事!

  树状数组不可能暴力清零!记个time或者依葫芦画瓢减回去都得以,一定无法清零!

  不要在CDQ里面套sort,太慢辣!(一定进不了第一版的!)

 

有关部分要小心的地点

  归并自然要把结余的搞完!每一遍自我都遗忘那码子事!

  树状数组不能暴力清零!记个time或者依葫芦画瓢减回去都得以,一定无法清零!

  不要在CDQ里面套sort,太慢辣!(一定进不了第一版的!)

 

相关文章