首页 > 其他 > 详细

【模板】三维偏序总结——动态开点树套树做法+cdq分治做法

时间:2020-05-23 19:46:33      阅读:70      评论:0      收藏:0      [点我收藏+]

三维偏序:对于每个点P(x,y,z), 统计所有P‘(x‘<=x,y‘<=y,z‘<=z)的某些信息

动态开点树套树确实比较强大,既能做三维偏序的求最值,又能做三维偏序求和

求最值 题目链接 https://acm.ecnu.edu.cn/contest/273/problem/C/

技术分享图片
#include <bits/stdc++.h>
using namespace std;
#define ll unsigned long long 
const int N = 2e7 + 5e6;

struct Pt {
    ll a, b, c, ans;
    int id;
    bool operator<(const Pt &x) const { 
        if(a!=x.a)return a < x.a; 
        if(b!=x.b)return b<x.b;
        return c<x.c;
    }
} p[N];

ll v[N],cnt;
int aa[N], ab[N], ac[N];
int ida, idb, idc;
int n;
int ans[N];

namespace segc {
    int val[N], ch[N][2], root[N], ind;
    void pushup(int p) { val[p] = max(val[ch[p][0]] , val[ch[p][1]]); }
    void modify(int p, int l, int r, int pos,int v) {
        if (l == r) {
            val[p]=v;
        } else {
            if (pos <= (l + r) / 2) {
                if (!ch[p][0]){
                    ch[p][0] = ++ind;
                    val[ind]=-0x3f3f3f3f;
                }
                modify(ch[p][0], l, (l + r) / 2, pos, v);
            } else {
                if (!ch[p][1]){
                    ch[p][1] = ++ind;
                    val[ind]=-0x3f3f3f3f;
                }
                modify(ch[p][1], (l + r) / 2 + 1, r, pos, v);
            }
            pushup(p);
        }
    }
    void modify(int ver, int pos,int v) {
        if (!root[ver])
            root[ver] = ++ind;
        modify(root[ver], 1, cnt, pos, v);
    }
    int query(int p, int l, int r, int ql, int qr) {
        if (l > qr || r < ql || p == 0)
            return -0x3f3f3f3f;
        if (l >= ql && r <= qr)
            return val[p];
        return max(query(ch[p][0], l, (l + r) / 2, ql, qr) , query(ch[p][1], (l + r) / 2 + 1, r, ql, qr));
    }
    int query(int ver, int ql, int qr) { return query(root[ver], 1, cnt, ql, qr); }
}

namespace segb {
    void modify(int p, int l, int r, int pos, int posi, int v) {
        segc::modify(p, posi, v);
        if (l != r) {
            if (pos <= (l + r) / 2)
                segb::modify(p * 2, l, (l + r) / 2, pos, posi, v);
            else
                segb::modify(p * 2 + 1, (l + r) / 2 + 1, r, pos, posi, v);
        }
    }
    void modify(int pos, int posi,int v) { modify(1, 1, cnt, pos, posi, v); }
    int query(int p, int l, int r, int ql, int qr, int iql, int iqr) {
        if (l > qr || r < ql)return -0x3f3f3f3f;
        if (l >= ql && r <= qr)return segc::query(p, iql, iqr);
        return max(query(p * 2, l, (l + r) / 2, ql, qr, iql, iqr),
               query(p * 2 + 1, (l + r) / 2 + 1, r, ql, qr, iql, iqr));
    }
    int query(int ql, int qr, int iql, int iqr) { return query(1, 1, cnt, ql, qr, iql, iqr); }
}

int k;


ll k1,k2;
unsigned long long CoronavirusBeats() {
unsigned long long k3 = k1, k4 = k2;
    k1 = k4;
    k3 ^= k3 << 23;
    k2 = k3 ^ k4 ^ (k3 >> 17) ^ (k4 >> 26);
    return k2 + k4;
}

signed main() {
    scanf("%d", &n);cin>>k1>>k2;
    for(int i=1;i<=n;i++){
        p[i].a=CoronavirusBeats();
        p[i].b=CoronavirusBeats();
        p[i].c=CoronavirusBeats();
        v[++cnt]=p[i].a;
        v[++cnt]=p[i].b;
        v[++cnt]=p[i].c;
        p[i].id=i; 
    }
    sort(v+1,v+1+cnt);
    cnt=unique(v+1,v+1+cnt)-v+1;
    for(int i=1;i<=n;i++){
        p[i].a=lower_bound(v+1,v+1+cnt,p[i].a)-v;
        p[i].b=lower_bound(v+1,v+1+cnt,p[i].b)-v;
        p[i].c=lower_bound(v+1,v+1+cnt,p[i].c)-v;
    }

    sort(p + 1, p + n + 1);

    int mx=0;
    for(int i=1;i<=n;i++){
        int res=segb::query(1,p[i].b,1,p[i].c);
        if(res>=0)res++;
        else res=0;
        p[i].ans=res;
        mx=max(mx,res+1);
        segb::modify(p[i].b,p[i].c,p[i].ans);
    }
    cout<<mx<<\n;
    for(int i=1;i<=n;i++)ans[p[i].id]=p[i].ans;
    for (int i = 1; i <= n; i++) printf("%d\n", ans[i]);
}
View Code

求和 题目:bzoj陌上花开(老经典题了)

技术分享图片
/*常规操作是每个结点动态开一个线段树,线段树的域维护的是集合的的域,即这个结点对应的集合信息存在该线段树里,
当合并两个结点时,只要合并两棵线段树即可*/
struct Node {
    ll ls,rs,ans,sum; 
}T[N<<6];
void update(ll &now,int l,int r,int pos){
    if(!now)now=++tot;
    if(l==r){
        T[now].ans=l;
        T[now].sum++;    
        return;
    }
    int mid=l+r>>1;
    if(pos<=mid)update(T[now].ls,l,mid,pos);
    else update(T[now].rs,mid+1,r,pos);
    pushup(now);
}
int merge(int x,int y,int l,int r){
    if(!x||!y)return x|y;
    int z=++tot;
    if(l==r){
        T[z].sum=T[x].sum+T[y].sum;
        T[z].ans=l;
        return z;
    }
    int mid=l+r>>1;
    T[z].ls=merge(T[x].ls,T[y].ls,l,mid);
    T[z].rs=merge(T[x].rs,T[y].rs,mid+1,r);
    pushup(z);
    return z;
}
int dfs(int u,int pre){
    update(rt[u],1,100000,c[u]);
    for(int i=0;i<G[u].size();i++){
        int v=G[u][i];
        if(v==pre)continue;
        dfs(v,u);
        rt[u]=merge(rt[u],rt[v],1,100000);
    }
    ans[u]=T[rt[u]].ans;
}
View Code

 

cdq分治+线段树做三维偏序,由于cdq分治本身的特性,只能将左区间的贡献统计到右区间中,所以不能用来求最值,只能求和

技术分享图片
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<queue>
#include<stack>
#include<cmath>
using namespace std;
typedef long long ll;

const int maxn = 200010;

int n,k,cnt,ans=0;

struct Node{
    int a,b,c,v,id;
}a[maxn],b[maxn],lc[maxn],rc[maxn];

int s[maxn],f[maxn],d[maxn];

void add(int x,int v){ for(int i=x;i<=k;i+=i&(-i)) s[i]+=v; }
int sum(int x){ int res=0; for(int i=x;i>0;i-=i&(-i)) res+=s[i]; return res; }
void clear(int x){ for(int i=x;i<=k;i+=i&(-i)) s[i]=0; }

bool cmpx(Node a,Node b){ return a.a!=b.a?a.a<b.a:a.b!=b.b?a.b<b.b:a.c<b.c;  }
bool cmpy(Node a,Node b){ return a.b!=b.b?a.b<b.b:a.c<b.c; }

void cdq(int l,int r){
    if(l==r) return;
    int mid=(l+r)/2;
    cdq(l,mid),cdq(mid+1,r);
    
    for(int i=l;i<=mid;i++) lc[i]=b[i];
    for(int i=mid+1;i<=r;i++) rc[i]=b[i];

    sort(lc+l,lc+1+mid,cmpy);
    sort(rc+mid+1,rc+r+1,cmpy);

    int p=l,q=mid+1;
    
    while(p<=mid&&q<=r){
        if(lc[p].b<=rc[q].b){
            add(lc[p].c,lc[p].v);
            ++p;
        }
        else{
            f[rc[q].id]+=sum(rc[q].c);
            ++q;
        }
    }
    while(q<=r){
        f[rc[q].id]+=sum(rc[q].c);
        ++q;
    }

    for(int i=l;i<=mid;i++){
        clear(lc[i].c);
    }
}

ll read(){ ll s=0,f=1; char ch=getchar(); while(ch<0 || ch>9){ if(ch==-) f=-1; ch=getchar(); } while(ch>=0 && ch<=9){ s=s*10+ch-0; ch=getchar(); } return s*f;}

int main(){
    n=read(),k=read();
    for(int i=1;i<=n;i++){ a[i].a=read(),a[i].b=read(),a[i].c=read(),a[i].id=i; }
    sort(a+1,a+1+n,cmpx);
    
    for(int i=1;i<=n;i++){
        if(a[i].a==a[i-1].a&&a[i].b==a[i-1].b&&a[i].c==a[i-1].c){
            ++b[cnt].v;
        }else{
            b[++cnt].a=a[i].a,b[cnt].b=a[i].b,b[cnt].c=a[i].c,b[cnt].id=a[i].id;
            b[cnt].v=1;
        }
    }

    
    cdq(1,cnt);

    for(int i=1;i<=cnt;i++){
        d[f[b[i].id]+b[i].v-1]+=b[i].v;
    }
    for(int i=0;i<=n-1;i++){
        printf("%d\n",d[i]);
    }

    return 0;
}
View Code

 

【模板】三维偏序总结——动态开点树套树做法+cdq分治做法

原文:https://www.cnblogs.com/zsben991126/p/12943930.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!