Loading

正睿做题笔记——简单平衡树

平衡树基本应用:

T1

模板题 1模板题 2

不多说,直接上 FHQ 板子就可以。一般来说,不写 LCT 的话,我一般都用 FHQ。

代码就不挂了。

T2

P1486

直接上 FHQ 板子即可,这个题就是 FHQ 的模板题,因为 FHQ 支持按照权值分裂。

代码:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 100010
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

template<typename T> inline T Min(T a,T b){
    return a<b?a:b;
}

inline int random(int n){
    return 1ll*rand()*rand()%n+1;
}

int n,Minn,Money;

struct node{
    int minn,val,size,key,l,r;
};

struct FHQ_Treap{
    node p[N];int tot,root;
    inline FHQ_Treap(){p[0].minn=-INF;}
    inline int NewNode(int val){
        p[++tot].val=val;p[tot].key=random(INF);
        p[tot].size=1;p[tot].minn=val;p[tot].l=p[tot].r=0;return tot;
    }
    inline void PushUp(int k){
        p[k].size=p[p[k].l].size+p[p[k].r].size+1;
        p[k].minn=Min(p[k].val,Min(p[p[k].l].minn,p[p[k].r].minn));
    }
    inline void Split(int k,int val,int &x,int &y){
        if(k==0){x=y=0;return;}
        if(p[k].val<=val){x=k;Split(p[k].r,val,p[k].r,y);}
        else{y=k;Split(p[k].l,val,x,p[k].l);}PushUp(k);
    }
    inline int Merge(int a,int b){
        if(!a||!b) return a+b;
        if(p[a].key>p[b].key){p[a].r=Merge(p[a].r,b);PushUp(a);return a;}
        else{p[b].l=Merge(a,p[b].l);PushUp(b);return b;}
    }
    inline void TreeInsert(int val){
        val-=Money;int x,y;Split(root,val,x,y);root=Merge(Merge(x,NewNode(val)),y);
    }
    inline void Delete(){
        if(p[root].minn+Money<Minn){int x,y;Split(root,Minn-Money-1,x,y);root=y;}
    }
    inline int Find(int k,int rank){
        if(p[p[k].r].size+1==rank) return p[k].val;
        else if(p[p[k].r].size+1>=rank) return Find(p[k].r,rank);
        else return Find(p[k].l,rank-1-p[p[k].r].size);
    }
    inline int TreeFind(int rank){
        if(p[root].size<rank) return -1;else return Find(root,rank)+Money;
    }
    inline int GetCnt(){
        return tot-p[root].size;
    }
};
FHQ_Treap tr;

inline char Get(){
    char c=getchar();while(c<'A'||c>'Z') c=getchar();return c;
}

int main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    read(n);read(Minn);
    for(int i=1;i<=n;i++){
        char c;int k;c=Get();read(k);
        if(c=='I'){if(k>=Minn) tr.TreeInsert(k);}
        else if(c=='A') Money+=k;
        else if(c=='S') Money-=k;
        else if(c=='F') printf("%d\n",tr.TreeFind(k));
        tr.Delete();
    }
    printf("%d\n",tr.GetCnt());
    return 0;
}

T3

链接

这个题直接上平衡树,以左端点为关键字插入平衡树,然后考虑若插入区间的话,把这段区间内的左端点全部删掉,因为原先的线段不想交,所以仅仅会有至多一条线段相交,特判一下就可以了。

代码:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 200010
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

inline int random(int n){return 1ll*rand()*rand()%n+1;}

struct FHQ_Treap{
    #define ls p[k].l
    #define rs p[k].r
    struct node{
        int val,size,key,l,r,rval;
        inline node(){}
        inline node(int val,int size,int key,int l,int r,int rval) : 
            val(val),size(size),key(key),l(l),r(r),rval(rval) {}
    }p[N];
    int tot,root;
    inline void Init(){tot=root=0;}
    inline int NewNode(int lv,int rv){p[++tot]=node(lv,1,random(INF),0,0,rv);return tot;}
    inline void PushUp(int k){p[k].size=p[ls].size+p[rs].size+1;}
    inline void Split(int k,int val,int &a,int &b){
        if(!k){a=b=0;return;}
        if(p[k].val<=val){a=k;Split(p[k].r,val,p[a].r,b);}
        else{b=k;Split(p[k].l,val,a,p[b].l);}PushUp(k);
    }
    inline int Merge(int a,int b){
        if(!a||!b) return a+b;
        if(p[a].key<=p[b].key){p[b].l=Merge(a,p[b].l);PushUp(b);return b;}
        else{p[a].r=Merge(p[a].r,b);PushUp(a);return a;}
    }
    inline int Insert(int lv,int rv){
        int x,y,z;Split(root,lv-1,x,y);Split(y,rv,y,z);
        int now=x;while(p[now].r) now=p[now].r;
        int ans=p[y].size;
        if(p[now].rval>=lv){
            int a,b;Split(x,p[now].val-1,a,b);
            x=a;ans++;
        }
        root=Merge(Merge(x,NewNode(lv,rv)),z);return ans;
    }
    inline int GetNum(){return p[root].size;}
}tr;


int n;

// #define fre

int main(){
    #ifdef fre
        freopen("my.in","r",stdin);
        freopen("my.out","w",stdout);
    #endif
    ios::sync_with_stdio(false);cin.tie(0);
    cin>>n;
    for(int i=1;i<=n;i++){
        char c;cin>>c;
        if(c=='A'){
            int l,r;cin>>l>>r;cout<<tr.Insert(l,r)<<'\n';
        }
        else cout<<tr.GetNum()<<'\n';
    }
    return 0;
}

平衡树维护区间操作

T1

模板题

平衡树维护区间翻转,我们用 FHQ 来维护序列,用下标当做关键字,然后对于一段区间来说,我们可以用 FHQ 按照大小分裂把这段区间分离出来,然后再根节点上打一个标记,然后再下传。注意到交换这段区间等价于我们交换这颗子树中的所有结点的左右儿子,这是因为一个序列相当于区间中序遍历,交换所有结点左右字数就是把中序遍历的序列交换过来。为了复杂度正确,我们在根节点打上标记,然后在遍历到这个节点的时候把标记下传即可。

一开始认为平衡树打标记可以先打上,下传的时候再操作,但这样是不对的,我们应该先操作,再打标记,和线段树一样。

代码:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 100010
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}
// #define fre

inline int random(int n){return 1ll*rand()*rand()%n+1;}

struct FHQ_Treap{
    #define ls p[k].l
    #define rs p[k].r
    struct node{
        int size,val,l,r,key,lazy;
        inline node(){}
        inline node(int size,int val,int l,int r,int key,int lazy) : size(size),val(val),l(l),r(r),key(key),lazy(lazy) {}
    }p[N];
    int tot,root;
    inline void Init(){tot=0;}
    inline int NewNode(int val){p[++tot]=node(1,val,0,0,random(INF),0);return tot;}
    inline void PushUp(int k){p[k].size=p[ls].size+p[rs].size+1;}
    inline void PushDown(int k){if(p[k].lazy){R(ls);R(rs);p[k].lazy=0;}}
    inline void Split(int k,int size,int &x,int &y){
        if(!k){x=0;y=0;return;}PushDown(k);
        if(p[ls].size+1<=size){x=k;Split(p[k].r,size-p[ls].size-1,p[x].r,y);PushUp(x);}
        else{y=k;Split(p[k].l,size,x,p[y].l);PushUp(y);}
    }
    inline int Merge(int a,int b){
        if(!a||!b) return a+b;
        if(p[a].key<=p[b].key){PushDown(b);p[b].l=Merge(a,p[b].l);PushUp(b);return b;}
        else{PushDown(a);p[a].r=Merge(p[a].r,b);PushUp(a);return a;}
    }
    inline void Insert(int posi,int val){
        int x,y;Split(root,posi-1,x,y);root=Merge(Merge(x,NewNode(val)),y);
    }
    inline void R(int k){swap(ls,rs);p[k].lazy^=1;}
    inline void Reverse(int l,int r){
        int x,y,z;Split(root,l-1,x,y);Split(y,r-l+1,y,z);R(y);root=Merge(Merge(x,y),z);
    }
    inline void Print(int k){
        if(!k) return;PushDown(k);Print(p[k].l);printf("%d ",p[k].val);Print(p[k].r);
    }
    inline void TreePrint(){Print(root);}
}tr;

int n,m;

int main(){
    #ifdef fre
        freopen("my.in","r",stdin);
        freopen("my.out","w",stdout);
    #endif
    read(n);read(m);tr.Init();
    for(int i=1;i<=n;i++) tr.Insert(i,i);
    for(int i=1;i<=m;i++){
        int l,r;read(l);read(r);tr.Reverse(l,r);
    }
    tr.TreePrint();
    return 0;
}

T2

链接

一道复杂的平衡树维护序列的题目。需要维护插入,删除,区间覆盖,区间翻转,区间查询和,查询最大子段和。

首先前两个操作不难维护,只需要注意因为要插入多个数据,所以我们要先把插入的这些树新建一棵树,然后再插入进行合并。

区间覆盖,区间翻转,打标记就可以,然后可以添加一点小 trick,比如说如果有区间覆盖,就可以不区间翻转。

区间查询和也很好做,我们考虑如何查询最大子段和。

我们只需要向线段树那样维护就可以,也就是维护前缀最大,后缀最大,然后整段和,在 PushUp 的时候更新信息就可以。具体实现看代码。

代码:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 500010
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}
template<typename T> inline T Max(T a,T b){return a<b?b:a;}

inline int random(int n){return 1ll*rand()*rand()%n+1;}

struct node{
    int val,MaxAns,MaxPre,MaxSuf,CoverLazy,ReverseLazy,size,l,r,key,sum;
    inline node(){}
    inline void Init(int v){
        size=1;val=v;sum=v;l=r=0;key=random(INF);MaxAns=val;MaxPre=MaxSuf=Max(val,0);
        CoverLazy=-INF;ReverseLazy=0;
    }
    inline void Print(){
        printf("size:%d val:%d sum:%d MaxAns:%d MaxPre:%d MaxSuf:%d l:%d r:%d co:%d re:%d\n",
        size,val,sum,MaxAns,MaxPre,MaxSuf,l,r,CoverLazy,ReverseLazy);
    }
}p[N];
int tot,root,sta[N],top;

struct FHQ_Treap{
    #define ls p[k].l
    #define rs p[k].r
    inline void Init(){tot=top=root=0;p[0].MaxAns=-INF;}
    inline int NewNode(int val){
        assert(tot<=500000);
        int now;if(top) now=sta[top--];else now=++tot;p[now].Init(val);
        return now;
    }
    inline void PushUp(int k){
        p[k].size=p[ls].size+p[rs].size+1;p[k].sum=p[ls].sum+p[rs].sum+p[k].val;
        p[k].MaxAns=Max(Max(p[ls].MaxAns,p[rs].MaxAns),
            Max(p[ls].MaxSuf+p[rs].MaxPre+p[k].val,p[k].val));
        p[k].MaxSuf=Max(p[rs].MaxSuf,(p[ls].MaxSuf+p[k].val+p[rs].sum));
        p[k].MaxPre=Max(p[ls].MaxPre,(p[rs].MaxPre+p[k].val+p[ls].sum));
    }
    inline void C(int k,int val){
        p[k].val=val;p[k].sum=p[k].size*val;p[k].MaxAns=Max(p[k].sum,p[k].val);
        p[k].CoverLazy=val;p[k].MaxPre=p[k].MaxSuf=Max(p[k].sum,0);
    }
    inline void R(int k){swap(ls,rs);swap(p[k].MaxSuf,p[k].MaxPre);p[k].ReverseLazy^=1;}
    inline void PushDown(int k){
        if(p[k].ReverseLazy){R(ls);R(rs);p[k].ReverseLazy=0;}
        if(p[k].CoverLazy!=-INF){
            if(ls) C(ls,p[k].CoverLazy);
            if(rs) C(rs,p[k].CoverLazy);p[k].CoverLazy=-INF;
        }
    }
    inline void Split(int k,int size,int &x,int &y){
        if(!k){x=y=0;return;}PushDown(k);
        if(p[ls].size+1<=size){x=k;Split(p[k].r,size-p[ls].size-1,p[x].r,y);}
        else{y=k;Split(p[k].l,size,x,p[y].l);}PushUp(k);
    }
    inline int Merge(int a,int b){
        if(!a||!b) return a+b;
        if(p[a].key<=p[b].key){PushDown(b);p[b].l=Merge(a,p[b].l);PushUp(b);return b;}
        else{PushDown(a);p[a].r=Merge(p[a].r,b);PushUp(a);return a;}
    }
    inline int Build(int l,int r,int *c){
        if(l==r){return NewNode(c[l]);}
        int mid=(l+r)>>1;return Merge(Build(l,mid,c),Build(mid+1,r,c));
    }
    inline void Insert(int posi,int len,int *c){
        int x,y;Split(root,posi,x,y);
        root=Merge(Merge(x,Build(1,len,c)),y);
    }
    inline void DeleteDfs(int k){
        if(!k) return;
        DeleteDfs(p[k].l);DeleteDfs(p[k].r);
        sta[++top]=k;
    }
    inline void Delete(int posi,int len){
        int x,y,z;Split(root,posi-1,x,y);Split(y,len,y,z);
        root=Merge(x,z);
        DeleteDfs(y);
    }
    inline void Cover(int posi,int len,int val){
        int x,y,z;Split(root,posi-1,x,y);Split(y,len,y,z);C(y,val);root=Merge(Merge(x,y),z);
    }  
    inline void Reverse(int posi,int len){
        int x,y,z;Split(root,posi-1,x,y);Split(y,len,y,z);R(y);root=Merge(Merge(x,y),z);
    }
    inline int GetSum(int posi,int len){
        int x,y,z;Split(root,posi-1,x,y);Split(y,len,y,z);
        int ans=p[y].sum;root=Merge(Merge(x,y),z);return ans;
    }
    inline void Print(int k){
        if(!k) return;
        PushDown(k);
        Print(p[k].l);printf("k:%d ",k); p[k].Print();Print(p[k].r);
    }
    inline void Print2(int k){
        if(!k) return;PushDown(k);Print2(p[k].l);printf("k:%d ",p[k].val);Print2(p[k].r);
    }
    inline void TreePrint(){printf("root:%d\n",root); Print(root);puts("");}
    inline void TreePrint2(){Print2(root);puts("");}
    inline int GetAns(){return p[root].MaxAns;}
}tr;

int n,m,a[N];

// #define fre

int main(){
    #ifdef fre
        freopen("my.in","r",stdin);
        freopen("my.out","w",stdout);
    #endif
    read(n);read(m);tr.Init();
    for(int i=1;i<=n;i++) read(a[i]);tr.Insert(0,n,a);
    for(int i=1;i<=m;i++){
        string s;cin>>s;int posi,len,val;
        // printf("p0: ");p[0].Print();
        if(s=="INSERT"){read(posi);read(len);for(int i=1;i<=len;i++) read(a[i]);tr.Insert(posi,len,a);}
        else if(s=="DELETE"){read(posi);read(len);tr.Delete(posi,len);}
        else if(s=="MAKE-SAME"){read(posi);read(len);read(val);tr.Cover(posi,len,val);}
        else if(s=="REVERSE"){read(posi);read(len);tr.Reverse(posi,len);}
        else if(s=="GET-SUM"){read(posi);read(len);printf("%d\n",tr.GetSum(posi,len));}
        else if(s=="MAX-SUM"){printf("%d\n",tr.GetAns());}
        // tr.TreePrint();
    }
    // tr.TreePrint2();
    return 0;
}

平衡树优化 dp

链接

我们有一个比较简单的 \(n^2\) dp:

\(f_{i,j}\) 表示前 \(i\) 个数选出 \(j\) 个的最大值,转移为 \(f_{i,j}=\max(f_{i-1,j},f_{i-1,j-1}+j\times a_i)\)

不难发现 \(f_{i,j}\) 如果把 \(j\) 看做自变量的话,这个函数是一个凸包,然后后面的转移就相当于合并两个凸包,不难证明这两个凸包只有一个交点,我们直接二分出这个交点,转移即可。细节较多,尤其是二分那里。如果会平衡树上二分的话可以少一个 \(\log\)

代码:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 100010
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

template<typename T> inline T Max(T a,T b){return a<b?b:a;}

inline int random(int n){return 1ll*rand()*rand()%n+1;}

struct Node{
    int ls,rs,val,Lazy1,Lazy2,Size,key;
    int maxx;
    inline Node(){}
}p[N];

int rt,tot;

#define ls(k) p[k].ls
#define rs(k) p[k].rs
struct FHQ_Treap{
    inline FHQ_Treap(){NewNode(0);rt=1;}
    inline int NewNode(int val){
        ++tot;ls(tot)=rs(tot)=0;p[tot].Lazy1=p[tot].Lazy2=0;p[tot].Size=1;p[tot].key=random(INF);
        p[tot].val=val;p[tot].maxx=val;return tot;
    }
    inline void PushUp(int k){p[k].Size=p[ls(k)].Size+p[rs(k)].Size+1;p[k].maxx=Max(p[k].val,Max(p[ls(k)].maxx,p[rs(k)].maxx));}
    inline void PushDown(int k){
        A(ls(k),p[k].Lazy1,p[k].Lazy2);A(rs(k),p[k].Lazy1,p[k].Lazy2+p[k].Lazy1*(p[ls(k)].Size+1));p[k].Lazy1=p[k].Lazy2=0;
    }
    inline void A(int k,int lz1,int lz2){
        p[k].Lazy1+=lz1;p[k].Lazy2+=lz2;p[k].val+=lz2+lz1*(p[ls(k)].Size+1);
    }
    inline int Merge(int a,int b){
        if(!a||!b) return a+b;
        PushDown(a);PushDown(b);
        if(p[a].key<p[b].key){ls(b)=Merge(a,ls(b));PushUp(b);return b;}
        else{rs(a)=Merge(rs(a),b);PushUp(a);return a;}
    }
    inline void Split(int k,int &x,int &y,int Size){
        if(!k){x=y=0;return;}
        PushDown(k);
        if(p[ls(k)].Size+1<=Size){x=k;Split(rs(k),rs(x),y,Size-p[ls(k)].Size-1);}
        else{y=k;Split(ls(k),x,ls(y),Size);}PushUp(k);
    }
    inline void Insert(int res,int val){
        int x,y;Split(rt,x,y,res);rt=Merge(Merge(x,NewNode(val)),y);
    }
    inline void Add(int posi,int lz1,int lz2){
        int x,y;Split(rt,x,y,posi);
        A(y,lz1,lz2);rt=Merge(x,y);
    }
    inline int GetVal(int k,int rank){
        if(!k) return 0;
        if(p[ls(k)].Size+1==rank) return p[k].val;
        else if(p[ls(k)].Size+1<rank) return GetVal(rs(k),rank-p[ls(k)].Size-1);
        else return GetVal(ls(k),rank);
    }
    inline int GetAns(int k){
        if(!k) return 0;
        int now=p[k].val;PushDown(k);
        return Max(Max(now,GetAns(ls(k))),GetAns(rs(k)));
    }
}tr;

int n;

int main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    read(n);
    for(int i=1;i<=n;i++){
        // printf("i=%d\n",i);
        int a;read(a);
        int l=0,r=i-1,res=i-1;
        while(l<=r){
            int mid=(l+r)>>1;
            if(tr.GetVal(rt,mid+1)+a*(mid+1)>tr.GetVal(rt,mid+2)){
                res=mid;r=mid-1;
            }
            else l=mid+1;
        }
        int t=tr.GetVal(rt,res+1);
        // printf("res=%d,t=%d\n",res,t);
        tr.Insert(res+1,t);
        tr.Add(res+1,a,res*a);
        // printf("%d %d %d %d %d %d\n",p[1].val,p[2].val,p[3].val,p[4].val,p[5].val,p[6].val);
    }
    // printf("tot=%d\n",tot);
    // printf("%d %d %d %d %d %d\n",p[1].val,p[2].val,p[3].val,p[4].val,p[5].val,p[6].val);
    printf("%d\n",tr.GetAns(rt));
    return 0;
}

平衡树维护凸包

T1

链接

我们一般使用 set 来代替平衡树,当然前提是没有其他操作。

有两种维护凸包的方式,一种是像 T1 这样,我们用坐标排序,这样做的好处是没有精度误差,但是实现较复杂,但也许是我实现的还不够精细,并且这种方法在维护一个凸壳的时候还可以做,如若维护一个凸包就有点复杂。

下一种 T2 再说。

对于一个坐标,我们在里面二分横坐标,然后叉积判断是否在凸包内部,不满足凸包性质的话就两边减,注意边界的处理。

代码:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 100010
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

typedef pair<dd,dd> P;
set<P> S;
P p[N];
int n,x,y,m,q;
dd ans[N<<1];

struct Ques{
    int op,which,time;
    inline bool operator < (const Ques &b) const{
        return time>b.time;
    }
}ques[N<<1];

dd nowans;

inline int Cross(P a,P b){return b.second*a.first-b.first*a.second;}
inline P Subt(P a,P b){return make_pair(a.first-b.first,a.second-b.second);}
inline dd Pow(dd a,int id){return a*a;}
inline dd GetDis(P a,P b){
    return sqrt(Pow((dd)(a.first-b.first),2)+Pow((dd)(a.second-b.second),2));
}

bool vis[N];

inline void Insert(int id){
    P now=p[id];
    // printf("now.x=%lf now.y=%lf\n",now.first,now.second);
    auto it=S.upper_bound(now);
    auto Right=it;auto Left=(--it);
    if(Cross(Subt((*Right),now),Subt((*Left),now))>=0) return;
    nowans-=GetDis((*Left),(*Right));
    // printf("Left.x=%lf Left.y=%lf Right.x=%lf Right.y=%lf\n",
        // Left->first,Left->second,Right->first,Right->second);
    while(it!=S.begin()){
        auto a=it;it--;
        if(Cross(Subt(now,(*a)),Subt((*it),(*a)))>=0){
            nowans=nowans-GetDis((*a),(*it));
            S.erase(a);
        }
        else break;
    }
    it=S.upper_bound(now);
    while((it)!=--S.end()){
        auto a=it;it++;
        if(Cross(Subt((*it),(*a)),Subt(now,(*a)))>=0){
            nowans=nowans-GetDis((*a),(*it));
            S.erase(a);
        }
        else break;
    }
    it=S.upper_bound(now);
    auto a=it,b=(--it);
    nowans+=GetDis((*b),now)+GetDis(now,(*a));
    // printf("a.x=%lf a.y=%lf b.x=%lf b.y=%lf\n",a->first,a->second,b->first,b->second);
    S.insert(now);
}

int main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    read(n);read(x);read(y);read(m);
    S.insert(make_pair(0,0));S.insert(make_pair(n,0));S.insert(make_pair(x,y));
    nowans+=GetDis(make_pair(0,0),make_pair(x,y));
    nowans+=GetDis(make_pair(x,y),make_pair(n,0));
    // printf("nowans:%lf\n",nowans);
    for(int i=1;i<=m;i++){read(p[i].first);read(p[i].second);}
    read(q);
    for(int i=1;i<=q;i++){
        read(ques[i].op);
        if(ques[i].op==1){read(ques[i].which);vis[ques[i].which]=1;}
        ques[i].time=i;
    }
    sort(ques+1,ques+q+1);
    for(int i=1;i<=m;i++){
        if(vis[i]) continue;
        // printf("i=%d\n",i);
        Insert(i);
    }
    // printf("nowans=%lf\n",nowans);
    for(int i=1;i<=q;i++){
        if(ques[i].op==1) Insert(ques[i].which);
        else ans[ques[i].time]=nowans;
    }
    for(int i=1;i<=q;i++){
        if(ans[i]) printf("%0.2lf\n",ans[i]);
    }
    return 0;
}

T2

链接

第二种适用于所有情况,就是一般我们先找到最初的三角形,在三角形内部取一点当做极点,然后按照极角序在 set 里放置,这样的话我们认为 set 里面是循环的,所以我们可以手动实现一个找到上一个或下一个迭代器。

需要注意这个极点必须在三角形内部,其次是精度问题,这里用的是 atan2 函数,如果担心精度问题就把坐标成比例翻倍即可。

代码:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define int long long
#define uint unsigned int
#define ull unsigned long long
#define N number
#define M number
using namespace std;
 
const int INF=0x3f3f3f3f;
 
template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

template<typename T> inline T Min(T a,T b){return a<b?a:b;}
template<typename T> inline T Max(T a,T b){return a<b?b:a;}
 
struct Node{
    ld x,y;
    ld horn;
    inline Node(){}
    inline Node(ld x,ld y,ld horn) : x(x),y(y),horn(horn) {}
    inline bool operator < (const Node &b)const{
        return horn<b.horn;
    }
    inline ld operator ^ (const Node &b)const{return x*b.y-y*b.x;}
    inline Node operator - (const Node &b)const{return Node(x-b.x,y-b.y,0);}
    inline void Print(){printf("%Lf %Lf\n",this->x,this->y);}
}p[4],O;
 
set<Node> S;
 
inline set<Node>::iterator Pre(set<Node>::iterator it){
    return it==S.begin()?--S.end():--it;
}
 
inline set<Node>::iterator Next(set<Node>::iterator it){
    return it==--S.end()?S.begin():++it;
}
 
int n;
 
inline void Insert(ld x,ld y){
    ld ho=atan2(y-O.y,x-O.x);
    Node now=Node(x,y,ho);
    auto it=S.lower_bound(now);
    if(it==S.end()) it=S.begin();
    auto it2=Pre(it);
    if((((*it)-now)^((*it2)-now))<=0) return;
    S.insert(now);it=S.find(now);
    auto it1=Pre(it);it2=Pre(it1);
    while((((*it1)-(*it2))^(now-(*it2)))<=0){S.erase(it1);it1=it2;it2=Pre(it1);}
    it1=Next(it);it2=Next(it1);
    while((((*it1)-(*it2))^(now-(*it2)))>=0){S.erase(it1);it1=it2;it2=Next(it1);}
}
 
inline bool Query(ld x,ld y){
    // printf("here\n");
    ld ho=atan2(y-O.y,x-O.x);
    Node now=Node(x,y,ho);
    auto it=S.lower_bound(now);
    if(it==S.end()) it=S.begin();
    auto it2=Pre(it);
    return (((*it)-now)^((*it2)-now))<=0;
}
 
signed main(){
    // freopen("my.in","r",stdin);
    // freopen("my.out","w",stdout);
    read(n);
    ld MaxX=-INF,MinX=INF,MaxY=-INF,MinY=INF;
    for(int i=1;i<=3;i++){
        int op,x,y;read(op);read(x);read(y);
        p[i].x=x;p[i].y=y;p[i].x*=3;p[i].y*=3;
    }
    O.x=(p[1].x+p[2].x)/2;O.y=(p[1].y+p[2].y)/2;
    O.x=(O.x+p[3].x)/2;O.y=(O.y+p[3].y)/2;
    // printf("%Lf %Lf\n",O.x,O.y);
    for(int i=1;i<=3;i++){
        p[i].horn=atan2(p[i].y-O.y,p[i].x-O.x);
        S.insert(p[i]);
    }
    for(int i=4;i<=n;i++){
        // printf("i=%d\n",i);
        int op,x,y;read(op);read(x);read(y);
        x*=3;y*=3;
        if(op==1) Insert(x,y);
        else puts(Query(x,y)==1?"YES":"NO");
    }
    return 0;
}

平衡树启发式合并

链接

线段树启发式合并其实没什么,就是合并两颗树的时候把小树一个一个插入到大树里即可。这样复杂度是 \(O(n\log^2 n)\) 的,用 Splay 据说可以减少一个 \(\log\)

注意并查集维护查询根。

代码:

#include<bits/stdc++.h>
#define dd double
#define ld long double
#define ll long long
#define uint unsigned int
#define ull unsigned long long
#define N 1000100
#define M number
using namespace std;

const int INF=0x3f3f3f3f;

template<typename T> inline void read(T &x) {
    x=0; int f=1;
    char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c == '-') f=-f;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    x*=f;
}

int fa[N],Root[N],tot,P[N];

inline int Find(int x){return fa[x]==x?x:fa[x]=Find(fa[x]);}
inline int random(int n){return 1ll*rand()*rand()%n+1;}

struct Node{
    int ls,rs,val,Size,key;
    inline Node(){}
    inline Node(int ls,int rs,int val,int Size,int key) : ls(ls),rs(rs),val(val),Size(Size),key(key) {}
}p[N];

#define ls(k) p[k].ls
#define rs(k) p[k].rs
struct FHQ_Treap{
    inline int NewNode(int val){++tot;p[tot]=Node(0,0,val,1,random(INF));return tot;}
    inline void PushUp(int k){p[k].Size=p[ls(k)].Size+p[rs(k)].Size+1;}
    inline void Split(int k,int &x,int &y,int val){
        if(!k){x=0;y=0;return;}
        if(p[k].val<=val){x=k;Split(rs(k),rs(x),y,val);PushUp(x);}else{y=k;Split(ls(k),x,ls(y),val);PushUp(y);}
    }
    inline int Merge(int a,int b){
        if(!a||!b) return a+b;
        if(p[a].key>p[b].key){rs(a)=Merge(rs(a),b);PushUp(a);return a;}
        else{ls(b)=Merge(a,ls(b));PushUp(b);return b;}
    }
    inline int Insert(int rt,int val){
        int x,y;Split(rt,x,y,val);
        // printf("here\n");printf("x=%d y=%d\n",x,y);
        return Merge(Merge(x,NewNode(val)),y);
    }
    inline int Insert(int rt,int val,int now){
        int x,y;Split(rt,x,y,val);p[now].ls=p[now].rs=0;p[now].Size=1;
        return Merge(Merge(x,now),y);
    }
    inline int GetVal(int k,int rank){
        if(!k) return -1;
        if(p[ls(k)].Size+1==rank) return k;
        else if(p[ls(k)].Size+1<rank) return GetVal(rs(k),rank-p[ls(k)].Size-1);
        else return GetVal(ls(k),rank);
    }
    inline void Dfs(int k,int &rt){
        if(ls(k)) Dfs(ls(k),rt);
        if(rs(k)) Dfs(rs(k),rt);
        rt=Insert(rt,p[k].val,k);
    }
    inline int MergeTree(int rt1,int rt2){
        if(p[Root[rt1]].Size>p[Root[rt2]].Size) swap(rt1,rt2);
        Dfs(Root[rt1],Root[rt2]);fa[rt1]=rt2;return rt2;
    }
    inline void Init(int n){
        for(int i=1;i<=n;i++){
            fa[i]=i;Root[i]=Insert(Root[i],P[i]);
        }
    }
}tr;

int n,m,q;

inline void Init(){
    read(n);read(m);
    for(int i=1;i<=n;i++) read(P[i]);
    // printf("here\n");
    tr.Init(n);
    // printf("here\n");
    for(int i=1;i<=m;i++){
        int from,to;read(from);read(to);
        if(from==0||to==0) continue;
        int faf=Find(from),fato=Find(to);
        tr.MergeTree(faf,fato);
    }
    read(q);
}

inline char GetChar(){
    char c=getchar();
    while(c!='B'&&c!='Q') c=getchar();
    return c;
}

signed main(){
    // freopen("P3224_10.in","r",stdin);
    // freopen("my.out","w",stdout);
    Init();
    // printf("here\n");
    for(int i=1;i<=q;i++){
        char c;c=GetChar();
        int A,B;read(A);read(B);
        if(c=='B'){
            if(A==0||B==0) continue;
            int faa=Find(A),fab=Find(B);
            if(faa==fab) continue;
            tr.MergeTree(faa,fab);
        }
        else{
            if(A==0||B==0){puts("-1");continue;}
            int faa=Find(A);
            printf("%d\n",tr.GetVal(Root[faa],B));
        }
    }
    return 0;
}
posted @ 2021-10-30 08:45  hyl天梦  阅读(85)  评论(0编辑  收藏  举报