这是一波强行总结。
下面是一波瞎比比。
这几天做了几道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)。
#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; }