[CH弱省胡策R2]TATT
cdq做四维偏序问题的模板。这道题的通过意味着我在一定程度上理解了cdq算法的本质。
我认为cdq本质上是对每个点的决策区间进行二进制分组以达到优化复杂度的目的。其实这和树状数组的思想或者说原理是相同的。至于分治,只不过是它的实现方式罢了。
另外一个突破就是我搞明白了初学时没搞清楚的一个问题,即何时应该先左后中再处理右半段。我们会发现求最长上升子序列的这个问题它需要满足一个条件,那就是假如我们知道从a可以更新到b,那么要满足两个条件。一是a比b小,另一个是我们知道a的答案(不然求出来就是错误的)。也就是说,我们在用左半段去更新右半段之前要保证左半段的答案已经求出来了,而求左半段的答案就要对左半段先进行cdq递归求解,于是表现在代码上就是先cdq了左半段,再处理左对右的贡献,最后再cdq右半段。至于求逆序对的问题,我们会发现无需求我们就可以知道某个点对比它大的点的贡献会是1,无需提前求出来,也就无需先左后中了。
复杂度上容易发现先左后中比先左后右多一个log,但其实这个log影响不大,没必要太在意这个小小的“常数”。
然后就是实现了。其实几维的偏序都是一样的,即用cdq搞掉几维之后用排序和数据结构处理掉最后两维即可。
放上我绝对没有压行的代码。
#include
#include
//#define zczc
#define cha if(s1.a^s2.a)return s1.a'9'){if(w=='-')f=-1;w=getchar();}
while(w<='9'&&w>='0'){wh=wh*10+w-'0';w=getchar();}wh*=f;return;}
int m,n,srp[N],nt,ti[N],t[N];
struct node{int a,b,c,d,num,ans;bool opa,opb;}a[N],b[N],c[N];
inline bool cmpa(node s1,node s2){cha;chb;chc;return s1.d>1;cdq2(l,mid);nt++;
for(int i=l;i<=r;i++)a[i].opb=(bool)(i>mid);sort(a+l,a+r+1,cmpc);
for(int i=l;i<=r;i++){
if(a[i].opa&&a[i].opb)check(a[i].ans,work(a[i].d));
if(a[i].opa==false&&a[i].opb==false)change(a[i].d,a[i].ans+a[i].num);
}
sort(a+l,a+r+1,cmpb);cdq2(mid+1,r);return;
}
void cdq1(int l,int r){
if(l==r)return;int mid=l+r>>1;cdq1(l,mid);for(int i=l;i<=r;i++)a[i].opa=(i>mid);
sort(a+l,a+r+1,cmpb);cdq2(l,r);sort(a+l,a+r+1,cmpa);cdq1(mid+1,r);return;
}
signed main(){
#ifdef zczc
freopen("in.txt","r",stdin);
#endif
read(m);for(int i=1;i<=m;i++){read(a[i].a);read(a[i].b);read(a[i].c);read(a[i].d);srp[i]=a[i].d;}
sort(srp+1,srp+m+1);n=unique(srp+1,srp+m+1)-srp-1;
for(int i=1;i<=m;i++)a[i].d=upper_bound(srp+1,srp+n+1,a[i].d)-srp-1;n=0;sort(a+1,a+m+1,cmpa);
for(int i=1;i<=m;i++){if(n!=0&&a[i]==a[n])a[n].num++;else a[++n]=a[i],a[n].num=1;}
cdq1(1,n);int ans=0;for(int i=1;i<=n;i++)check(ans,a[i].ans+a[i].num);
printf("%d",ans);return 0;
}