CDQ分治小结

P3810三维偏序(陌上花开)

一道CDQ分治的比较模板又不是模板的问题.

\(f_i\)表示\(a_j<=a_i\)\(b_j<=b_i\)\(c_j<=c_i\)\(j\)的数量

对于\(d\in[0,n)\)让你求\(f(i) == d\)的数量

其实个人感觉模板的话

还是严格小于比较好做

先来考虑一下严格小于该怎么做

我们先对整体按照\(a_i\)排序,这样的话我们进行的分治就满足了第一个限制

之后我们试想一下,如果要求左区间对于右区间的贡献

我们只需要将两边分别排序

虽然这样两边就不一定在满足\(a_i\)的限制,但是左右两个区间依旧满足(因为我们是分别排序)

直接用权值树状数组计算右边对与左边的贡献

但是,如果要求小于等于,就要考虑有两个值其\(a_I,b_i,c_i\)均相同的情况

这样的话我们就去重

将重复\(n\)个的元素的权值改为\(n\)

最后

\(cnt_{ai.ans + ai.val-1}+=ai.ans\)

即可

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<cctype>
#include<queue>
#include<stack>
using namespace std;
const int N = 1e5 + 3;
const int M = 2e5 + 3;
int n,k;
struct BIT{
    int c[M];
    inline int lowbit(int x){return x & -x;}
    inline void ins(int x,int v){
        for(;x <= k;x += lowbit(x)) c[x] += v;
    }
    inline int query(int x){
        int res = 0;
        for(;x;x -= lowbit(x)) res += c[x];
        return res; 
    }
}T;
struct node{
    int x,y,z;
    int val,ans;    
}a[N],b[N];
int cnt[N];
inline int read(){
    char ch = getchar();int v = 0,c = 1;
    while(!isdigit(ch)){
        if(ch == '-') c = -1;
        ch = getchar(); 
    }
    while(isdigit(ch)){
        v = v * 10 + ch - 48;
        ch = getchar(); 
    }
    return v * c;   
}
inline bool cmpx(node xx,node yy){
    if(xx.x != yy.x)
    return xx.x < yy.x;
    if(xx.y != yy.y)
    return xx.y < yy.y;
    return xx.z < yy.z;
}
inline bool cmpy(node xx,node yy){
    if(xx.y != yy.y)
    return xx.y < yy.y;
    return xx.z < yy.z;
}   
inline void solve(int l,int r){
    if(l == r) return ;
    int mid = (l + r) >> 1;
    solve(l,mid);solve(mid + 1,r);
//  printf("l:%d r:%d\n",l,r);
    sort(a + l,a + mid + 1,cmpy);
    sort(a + mid + 1,a + r + 1,cmpy);
//  for(int i = l;i <= r;++i) printf("%d %d\n",a[i].y,a[i].z);
    int top = l;
    for(int i = mid + 1;i <= r;++i){
        for(;top <= mid && a[top].y <= a[i].y;top++)
        T.ins(a[top].z,a[top].val);
    //  printf("%d %d %d\n",top,i,T.query(a[i].z));
        a[i].ans += T.query(a[i].z);
    }
    for(int i = l;i < top;++i) T.ins(a[i].z,-a[i].val);
}
int main(){
    n = read(),k = read();
    for(int i = 1;i <= n;++i)
        b[i].x = read(),b[i].y = read(),b[i].z = read();
    sort(b + 1,b + n + 1,cmpx);
    int c = 0,num = 0;
    for(int i = 1;i <= n;++i){
        c++;
        if(b[i].x != b[i + 1].x || b[i].y != b[i + 1].y || b[i].z != b[i + 1].z)
        a[++num] = b[i],a[num].val = c,c = 0;
    }
    swap(num,n);//for(int i = 1;i <= n;++i) printf("%d %d %d %d\n",a[i].x,a[i].y,a[i].z,a[i].val);
    solve(1,n);
//  for(int i = 1;i <= n;++i) printf("%d ",a[i].ans);puts("");
    for(int i = 1;i <= n;++i) cnt[a[i].ans + a[i].val - 1] += a[i].val;
    for(int i = 0;i < num;++i) printf("%d\n",cnt[i]);
    return 0;   
}

CF669E. Little Artem and Time Machine

CDQ的板子题了

我们以输入的操作顺序为第一维

操作的时间为第二维

第三维就直接查询有多少数等于他就好了

但是由于排序是将

sort(a + l,a + mid + 1,cmp)

写成了

sort(a + l + 1,a + mid + 1,cmp)

然后,调了一下午,还去找\(wqy\)学长丢人

#include<cstdio>
#include<cstring>
#include<cctype>
#include<map>
#include<algorithm>
#include<iostream>
#define LL long long
using namespace std;
const int N = 1e5 + 3;
int n;
struct Q{
    int type;
    LL time;
    LL val;
    int id;
    int ans;
}a[N];
map <LL,int> m;
inline LL read(){
    char ch = getchar();long long v = 0,c = 1;
    while(!isdigit(ch)){
        if(ch == '-') c = -1;
        ch = getchar(); 
    }
    while(isdigit(ch)){
        v = v * 10 + ch - 48;
        ch = getchar(); 
    }
    return v * c;   
}
inline bool cmp(Q x,Q y){
    return x.time < y.time; 
}
inline bool cmp2(Q x,Q y){
    return x.id < y.id;
}
inline void solve(int l,int r){
    if(l == r) return ;
    int mid = (l + r) >> 1;
    solve(l,mid);solve(mid + 1,r);
//  printf("l:%d r:%d\n",l,r);
    sort(a + l,a + mid + 1,cmp);
    sort(a + mid + 1,a + r + 1,cmp);
//  for(int i = l;i <= mid;++i) printf("%d %lld %lld\n",a[i].type,a[i].time,a[i].val);
//  printf("---------------\n");
//  for(int i = mid + 1;i <= r;++i) printf("%d %lld %lld\n",a[i].type,a[i].time,a[i].val);
    int top = l;
    for(int i = mid + 1;i <= r;++i){
        while(i <= r && a[i].type != 3) ++i;
        if(i > r) break;
        while(top <= mid && a[top].time <= a[i].time){
            if(a[top].type == 1) m[a[top].val]++;
            else if(a[top].type == 2) m[a[top].val]--;
            top++;
        }
        a[i].ans += m[a[i].val];
    }
    m.clear();
}
int main(){
    n = read();
    for(int i = 1;i <= n;++i){
        a[i].type = read();
        a[i].time = read();
        a[i].val = read();
        a[i].id = i;
    }
    solve(1,n);
    sort(a + 1,a + n + 1,cmp2); 
    for(int i = 1;i <= n;++i) if(a[i].type == 3) printf("%d\n",a[i].ans);
    return 0;