cdq partition-dimensional partial order problem CDQ partition summary (CDQ, Fenwick tree, merge sort)

Reprinted from FlashHu Gangster blog CDQ partition summary (CDQ, Fenwick tree, merge sort) , some telling deletion section, with its own code

 

CDQ idea of ​​partition

CDQ partition off divide and conquer algorithm is time-based. This type of partition has an important idea - a child with a problem to calculate the contribution to another sub-issues.

With this in mind, we can certainly solve the complexity of the scope of the partial order problem in the mainland. From a partial order (that is, in the table arrangement) $ \ Theta (N) $ start, a dimensional increase for each additional $ \ Theta (\ log N) $ times, the dimensions can be increased by superimposing cdq partition continuously, but when reached more than 4-dimensional efficiency and violence on the same subject.

 

Example 1

P3810 [template] dimensional partial sequence (Mo flowers)

I.e., given a number of elements, each element has three attribute value \ (A, B, C \) , asking for each element \ (I \) , satisfies \ (a_j \ leq a_i, b_j \ leq b_i, c_j \ leq C_i \) a \ (J \) number

Do not worry, start with a simple question to start

Imagine two partial order is \ (a_j \ leq a_i, b_j \ leq b_i \) how do

Press \ (a \) as the first key, \ (b \) as the second sort key, then we guarantee the first dimension \ (a \) ordered.

Thus, for each \ (I \) , may only be \ (1 \) to \ (i-1 \) element will contribute to it, then the Direct \ (1 \) to \ (i-1 \) element satisfies \ (b_j \ leq b_i \) is the number of elements.

Implementation? Dynamic Maintenance \ (B \) tree array, front to back again swept Well, \ (O (n-\ log n-) \) .

Then the three-dimensional partial order it? We guarantee only if the first two are met can calculate the answer.

Still pressing \ (a \) as the first key, \ (b \) for the second key, \ (c \) as the third sort key, the first dimension of less than or equal to the right to ensure that the left.

In order to ensure that the second dimension is less than or equal to the right to the left, we still need to sort.

Think of the process of merge sort is a divide and conquer, can we in the process of merging, the statistics generated in the sub-questions to answer contribute?

Now we have a sequence, we call it recursively divided into two sub-problems, sub-problems were completely merge sort, has pledged to \ (b \) ordered. At this time, there is a dividing line between the two sub-problems, a first original dimension less left to the right, so now the left boundary of any \ (A \) course is less than any one of the right. That does not mean that only the left side of the dividing line can contribute to generate the right?

So, the problem down to two dimensions. We can sort, merge sort (pointer to the left for the \ (J \) , the right to \ (i \) ) and maintenance (c \) \ tree array, if the current \ (b_j \ leq b_i \) explained \ (j \) can be added later to meet \ (c_j \ leq c_i \) is \ (i \) generates a contribution, the \ (c_j \) added Fenwick tree; otherwise, because the added behind \ ( j \) does not have \ (i \) generates a contribution, so just before the statistics are all contributed to the query Fenwick tree \ (c_i \) prefix and.

This is the answer to the problem of child statistics in the partition, there is what to do with the total answer? Easy to find, each sub-question statistics only contribution across the dividing line, and conversely, each capable of generating a contribution of \ (i, J \) , and only one sub-problems, both at the same time both are included, and in the opposite side of the dividing line. Then the contribution of all the sub-issues add up to the total answer.

General idea of the algorithm is such friends. As regards complexity, \ (T (n-) = O (n-\ log K) + T (\ FRAC n-2) = O (n-\ log n-\ log K) \) .

Of course, there are many details.

The biggest problem is that there may be exactly the same elements. In this case, they had each other have contributed, but the process of cdq only the left can contribute right. what should I do?

We go heavy sequence, so now is not the same. Now to a weight of each element value \ (v \) number of occurrences equal. The middle of the specific implementation process is also slightly changed, inserted in the tree array value is \ (v \) instead of \ (1 \) , and when the final tally answer, we must count the contribution of the same internal elements ans+=v-1.

On writing, in order to prevent the sort and merge sort spatially move too frequently, not for each element struct seal, so what should change function cmp

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int INF=1e9+7,MAXN=1e5+10,MAXK=2e5+10;
struct node{
    int x,y,z,ans,w;
}A[MAXN],tmp[MAXN];
inline bool cmpx(node x,node y){
    return x.x<y.x||(x.x==y.x&&(x.y<y.y||(x.y==y.y&&x.z<y.z)));
}
inline bool cmpy(node x,node y){
    return x.y<y.y||(x.y==y.y&&(x.x<y.x||(x.x==y.x&&x.z<y.z)));
}
int N,K,sum[MAXK];
inline int lowbit(int x){
    return x&-x;
}
inline void upd(int x,int c){
    while(x<=K){
        sum[x]+=c;
        x+=lowbit(x);
    }
}
inline int qry(int x){
    int ret=0;
    while(x){
        ret+=sum[x];
        x-=lowbit(x);
    }
    return ret;
}
void cdq(int l,int r){
    if(l==r)
        return;
    int mid=(l+r)>>1;
    cdq(l,mid);
    cdq(mid+1,r);
    sort(A+l,A+mid+1,cmpy);
    sort(A+mid+1,A+r+1,cmpy);
    int i=mid+1,j=l;
    for(;i<=r;i++){
        while(A[j].y<=A[i].y&&j<=mid){
            upd(A[j].z,A[j].w);
            j++;
        }
        A[i].ans+=qry(A[i].z);
    }
    for(i=l;i<j;i++)
        upd(A[i].z,-A[i].w);
}
int n,ans[MAXK];
int main(){
    scanf("%d%d",&n,&K);
    for(int i=1;i<=n;i++){
        scanf("%d%d%d",&tmp[i].x,&tmp[i].y,&tmp[i].z);
    }
    sort(tmp+1,tmp+n+1,cmpx);
    for(int i=1,c=1;i<=n;i++,c++)
        if((tmp[i].x^tmp[i+1].x)|(tmp[i].y^tmp[i+1].y)|(tmp[i].z^tmp[i+1].z)){
            A[++N]=tmp[i];
            A[N].w=c;
            c=0;
        }
    cdq(1,N);
    for(int i=1;i<=N;i++)
        ans[A[i].ans+A[i].w-1]+=A[i].w;
    for(int i=0;i<n;i++)
        printf("%d\n",ans[i]);
    return 0;
}

 

Two examples

Luo Gu P4169 [Violet] angel doll / SJY put the pieces

Luo Gu topic Portal

Not KDT, however CDQ certainly have an advantage.

At first glance you will be able to find every modification or query has three attributes, \ (the X-, the y-\) , as well as a time stamp. So how do you put it into the general three-dimensional partial order problem?

If you are in the lower left corner of the query point all the memories, it will only \ (x, y \) and timestamps three dimensions are smaller than memory point query point can produce contributions, which is a three-dimensional partial order.

What contribution is it? A plurality of \ (J \) of \ (I \) produces a contribution, then directly to the absolute value, the answer is \ (\ min \ {x_i-x_j-y_j y_i + \} \) , i.e. \ (x_i + y_i- \ max \ {x_j y_j + \} \) , or the array may be a tree, but changed to maintain maximum prefix. The first dimension timestamp, the input is already sorted; second place \ (x \) merge; third \ (y \) Fenwick tree statistical answer.

However, the assumption does not hold. But we can see that each point can produce contributions may be only in four directions query point (lower left, upper left, lower right, upper right), then all the points should be \ (3 \) over the coordinates Flip (New coordinate minus range equal to the original coordinates), do \ (4 \) times CDQ, you can count the best answer to each direction, and finally take \ (\ min \) can be.

\ (n-, m = 300000 \) , range \ (1000000 \) , a look at this \ (O ((n + m ) \ log (n + m) \ log k) \) big, have to run \ (4 \) times, it really does not T? So we should optimize some of the details.

First, konjac are still used in three-dimensional partial order template approach, there is no element to reduce the space sealed struct exchange, this can also be done to restore faster after flipping coordinate, direct foragain initialized. However, will bring an array of frequently called konjac also doubted the feasibility of this optimization, but also look Dalao advice.

Then, we found that the left has \ (n \) elements are identified by a memory point. That is, we do not have \ (n + m \) points CDQ ran, and only later \ (m \) th run, the foregoing \ (n-\) points directly by pretreatment \ (X \ ) a first key, \ (Y \) a second key sort, so that it drops to the complexity of the \ (O (n \ log n + m \ log m \ log k + n \ log k) \) a. However, after finishing flipped coordinates do not forget to handle it. If this is a flip \ (the y-\) , so \ (x \) will not be affected; if the flip is \ (x \) , then flip the array directly Jiuhaola!

As for what to spend fread Ye Hao. This plus a bunch of optimization, the code will have 90 line.

Then a hand in the 1A opinions! ? Usually accustomed to full screen bright red WA, konjac also have to sigh, compared to a lot of data structures, CDQ really good idea to write better tone board.

#include<cstdio>
#include<cstring>
#include<algorithm>
#define RG register
#define I inline void
#define R RG int
#define gc          if(++pp==pe)fread (pp=buf,1,SZ,stdin)
#define pc(C) *pp=C;if(++pp==pe)fwrite(pp=buf,1,SZ,stdout)
const int N=3e5,D=1e6+2,SZ=1<<19,INF=20020307;
char buf[SZ],*pe=buf+SZ,*pp=pe-1;
int x[N],y[N],p[N],q[N],f[N],ans[N],e[D+1];
bool t[N];
struct NODE{
    int x,y;
    inline bool operator<(RG NODE b)const{
        return x<b.x||(x==b.x&&y<b.y);
    }
}a[N];//前n个点
inline int in(){
    gc;while(*pp<'-')gc;
    R x=*pp&15;gc;
    while(*pp>'-'){x*=10;x+=*pp&15;gc;}
    return x;
}
I out(R x){
    if(x>9)out(x/10);
    pc(x%10|'0');
}
I min(R&x,R y){if(x>y)x=y;}
I upd(R i,R v){for(;i<=D;i+=i&-i)if(e[i]<v)e[i]=v;}
I qry(R i,R&v){for(;i   ;i-=i&-i)if(v<e[i])v=e[i];}
I clr(R i)    {for(;i<=D;i+=i&-i)e[i]=0;}
void cdq(R*p,R m){//三维偏序Dalao们都会吧
    if(m==1)return;
    R n=m>>1,i,j,k;
    cdq(p,n);cdq(p+n,m-n);
    memcpy(q,p,m<<2);
    for(k=i=0,j=n;i<n&&j<m;++k)
        if(x[q[i]]<=x[q[j]]){
            if(!t[q[i]])upd(y[q[i]],x[q[i]]+y[q[i]]);
            p[k]=q[i++];
        }
        else{
            if(t[q[j]])qry(y[q[j]],f[q[j]]);
            p[k]=q[j++];
        }
    for(;j<m;++j)
        if(t[q[j]])qry(y[q[j]],f[q[j]]);
    memcpy(p+k,q+i,(n-i)<<2);//注意收尾和清空
    for(--i;~i;--i)clr(y[q[i]]);
}
int main(){
    R n=in(),m=in(),i,j,k;
    for(i=0;i<n;++i)
        a[i].x=in()+1,a[i].y=in()+1;
    std::sort(a,a+n);//n个点预排序
    for(i=0;i<m;++I) {
         IF ((T [I] = in () - . 1 )) ANS [I] = INF; // Note that the maximum value to 
        X [I] = in () + . 1 ; Y [I] = in ( ) + . 1 ; // the bIT not have the subscript 0, so the change what 
    }
     for (K = . 1 ; K <= . 4 ; ++ K) {
         for (I = 0 ; I <m; I ++) P [I ] = I; // soon be initialization sequence 
        the cdq (P, m);
         for (I = J = 0 ; I <&& n-J <m;) { // out of statistics and CDQ still the same, but do not merge the 
            IF ( A [I] .x <=  X [P [J]])
                UPD (A [I] .y, A [I] .x+a[i].y),++i;
            else{
                if(t[p[j]])qry(y[p[j]],f[p[j]]);
                ++j;
            }
        }
        for(;j<m;++j)
            if(t[p[j]])qry(y[p[j]],f[p[j]]);
        memset(e,0,sizeof(e));
        for(i=0;i<m;++i)
            if(t[i]&&f[i])min(ans[i],x[i]+y[i]-f[i]),f[i]=0;
        if(k==4)break;
        if(k&1){//第一次、第三次上下翻
            for(i=0;i<n;++i)a[i].y=D-a[i].y;
            for(i=0;i<m;++i)y[i]=D-y[i];
        }
        else{//第二次左右翻
            for(i=0;i<n;++i)a[i].x=D-a[i].x;
            for(i=0;i<m;++i)x[i]=D-x[i];
            for(i=0,j=n-1;i<j;++i,--j)std::swap(a[i],a[j]);//注意仍要保证x不降
        }
    }
    for(pp=buf,i=0;i<m;++i)
        if(t[i]){out(ans[i]);pc('\n');}
    fwrite(buf,1,pp-buf,stdout);
    return 0;
}

Guess you like

Origin www.cnblogs.com/guoshaoyang/p/11233726.html