pb_ds模板

 

注意点都在注释里。

自行定义update规则的写法近期会补上。

#include <ext/pb_ds/assoc_container.hpp>

#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/hash_policy.hpp>
#include <ext/pb_ds/priority_queue.hpp>
using namespace std;
using namespace __gnu_pbds;

typedef int pii;

void tree_test()
{
    tree<pii,null_type,less<pii>,rb_tree_tag,tree_order_statistics_node_update> t,nt;
    
    //可遍历: for(xx::iterator it=t.begin();it!=t.end();it++)
    //一些正常的函数: t.clear(); t.empty(); t.find();
     
    //插入: t.insert(x);
    //所有元素不可重 
    
    //删除: t.erase(x);
    
    //排名: t.order_of_key(x);
    //返回【严格小于】x的数的个数 
    
    //kth: t.find_by_order(k);
    //返回从小到大第k个数的iterator【从0开始】 
    //当k>=sz时返回t.end() 
    
    //lower_bound: t.lower_bound(x);
    //*it>=x
    
    //upper_bound: t.upper_bound(x);
    //*it>x
    
    //合并: t.join(nt);
    //将nt合并进入t,之后nt清空 
    //t,nt需要类型相同,且值域不重合
    
    //分裂: t.split(x,nt);
    //将<=x的元素留在t中,其余放到nt中
    //会将nt中原有元素扔掉 
}

void priority_queue_test()
{
    //小根堆greater<pii> 大根堆less<pii>
    __gnu_pbds::priority_queue<pii,std::less<pii>,pairing_heap_tag> Q,nQ;
    
    //可遍历: for(xx::iterator it=Q.begin();it!=Q.end();it++)
    //遍历顺序与堆的性质相反
    
    //基本函数: Q.push(x); Q.pop(); Q.top(); Q.clear(); Q.empty(); Q.size();
    
    //合并: Q.join(nQ);
    //会把nQ清空 
}

void hash_test()
{
    gp_hash_table<string,int> mp;
    
    //可遍历: for(xx::iterator it=mp.begin();it!=mp.end();it++)
    //it->first it->second
    //【遍历顺序不按第一关键字】 
    
    //经典用法: mp["rlstxdy"]=666;
    //基本函数: mp.size(); mp.empty(); 
}

int main()
{
    tree_test();
    hash_test();
    priority_queue_test();
}

 

自定义tree:

自定义指的是,在每个节点维护除了key之外的其他信息(比如子树大小、子树key之和等)。

这是在node_update中通过重载()来实现的。由于每次平衡树操作都会调用一次(),所以信息能够一直被正确维护。

还可以访问tree中的节点、自定义函数,同样写在node_update中。在额外声明node_begin()、node_end()后,就可以方便地进行节点访问。

以下是一个在每个节点维护子树大小、子树key之和的例子。其中prefix函数返回小于等于$x$的元素数量与元素key之和。

#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
using namespace std;
using namespace __gnu_pbds;
 
typedef long long ll;
typedef pair<int,int> pii;
 
struct Node
{
    int num;
    ll sum;
    
    Node(int a=0,ll b=0)
    {
        num=a,sum=b;
    }
};
inline Node operator +(const Node &X,const Node &Y)
{
    return Node(X.num+Y.num,X.sum+Y.sum);
}
inline Node operator -(const Node &X,const Node &Y)
{
    return Node(X.num-Y.num,X.sum-Y.sum);
}
 
template <class Node_CItr,class Node_Itr,class Cmp_Fn,class _Alloc>
struct my_node_update
{
    typedef Node metadata_type;
    
    //it:当前指针  end_it:空指针 
    void operator()(Node_Itr it,Node_CItr end_it)
    {
        Node_Itr ls=it.get_l_child(); 
        Node_Itr rs=it.get_r_child();
        
        Node l,r;
        if(ls!=end_it) l=ls.get_metadata();
        if(rs!=end_it) r=rs.get_metadata();
        
        const_cast<Node&>(it.get_metadata())=Node(l.num+r.num+1,l.sum+r.sum+(**it).first);
    }
    
    Node prefix(pii x)
    {
        Node ans;
        Node_CItr it=node_begin();
        
        while(it!=node_end())
        {
            Node_CItr ls=it.get_l_child();
            Node_CItr rs=it.get_r_child();
            
            if(Cmp_Fn()(x,**it))
                it=ls;
            else
            {
                ans.num++;
                ans.sum+=(**it).first;
                
                if(ls!=node_end())
                    ans=ans+ls.get_metadata();
                it=rs;
            }
        }
        return ans;
    }
     
    virtual Node_CItr node_begin() const=0; //根节点 
    virtual Node_CItr node_end() const=0; //空节点 
};
typedef tree<pii,null_type,less<pii>,rb_tree_tag,my_node_update> mytree;

本来想写一个支持多次插入/删除同一key的版本(+/-cnt),后来发现在node_update中调用insert和erase不太方便,所以最后采用的办法是key用pair来存(第一维key、第二维id)、每次查询prefix的时候查询$pair(x,\infty)$。

 

一个例题:

Codeforces Gym 102832B  (The Tortoise and the Hare,2020CCPC沈阳)

需要写BIT套平衡树,支持查询小于等于$x$的元素数量与元素和。那么可以使用自定义tree实现。

KD树会慢2倍,BIT套动态开点线段树会MLE 3倍。以后也可以考虑考虑这种玩法。

#include <bits/stdc++.h>
#include <ext/pb_ds/tree_policy.hpp>
#include <ext/pb_ds/assoc_container.hpp>
using namespace std;
using namespace __gnu_pbds;

typedef long long ll;
typedef pair<int,int> pii;

struct Node
{
    int num;
    ll sum;
    
    Node(int a=0,ll b=0)
    {
        num=a,sum=b;
    }
};
inline Node operator +(const Node &X,const Node &Y)
{
    return Node(X.num+Y.num,X.sum+Y.sum);
}
inline Node operator -(const Node &X,const Node &Y)
{
    return Node(X.num-Y.num,X.sum-Y.sum);
}

template <class Node_CItr,class Node_Itr,class Cmp_Fn,class _Alloc>
struct my_node_update
{
    typedef Node metadata_type;
    
    //it:待更新节点  end_it:空节点 
    void operator()(Node_Itr it,Node_CItr end_it)
    {
        Node_Itr ls=it.get_l_child(); 
        Node_Itr rs=it.get_r_child();
        
        Node l,r;
        if(ls!=end_it) l=ls.get_metadata();
        if(rs!=end_it) r=rs.get_metadata();
        
        const_cast<Node&>(it.get_metadata())=Node(l.num+r.num+1,l.sum+r.sum+(**it).first);
    }
    
    Node prefix(pii x)
    {
        Node ans;
        Node_CItr it=node_begin();
        
        while(it!=node_end())
        {
            Node_CItr ls=it.get_l_child();
            Node_CItr rs=it.get_r_child();
            
            if(Cmp_Fn()(x,**it))
                it=ls;
            else
            {
                ans.num++;
                ans.sum+=(**it).first;
                
                if(ls!=node_end())
                    ans=ans+ls.get_metadata();
                it=rs;
            }
        }
        return ans;
    }
     
    virtual Node_CItr node_begin() const=0; //根节点 
    virtual Node_CItr node_end() const=0; //空节点 
};
typedef tree<pii,null_type,less<pii>,rb_tree_tag,my_node_update> mytree;

const int N=100005;
const int oo=1<<30;

int n,m,q;
int a[N];

inline int lowbit(int x)
{
    return x&(-x);
}

ll bit[N];

inline void add(int k,ll x)
{
    for(int i=k;i<=n;i+=lowbit(i))
        bit[i]+=x;
}

inline ll query(int k)
{
    ll res=0;
    for(int i=k;i;i-=lowbit(i))
        res+=bit[i];
    return res;
}

mytree t[N];

inline void add(int k,pii x)
{
    for(int i=k;i<=n;i+=lowbit(i))
        t[i].insert(x);
}

inline void erase(int k,pii x)
{
    for(int i=k;i<=n;i+=lowbit(i))
        t[i].erase(x);
}

inline Node query(int l,int r,int x)
{
    Node res;
    for(int i=r;i;i-=lowbit(i))
        res=res+t[i].prefix(pii(x,oo));
    for(int i=l-1;i;i-=lowbit(i))
        res=res-t[i].prefix(pii(x,oo));
    return res;
}

inline bool check(int l,int r,int k,int mid,ll psum)
{
    Node tmp=query(l,r,m-1-mid);
    int num=tmp.num;
    ll sum=tmp.sum;
    
    ll val=1LL*num*mid;
    val+=1LL*(r-l+1-num)*(m-1);
    val-=psum-sum;
    
    return (val>=1LL*(r-l+1-k)*mid);
}

void solve(int l,int r,int k)
{
    ll psum=query(r)-query(l-1);
    
    ll ans=0;
    if(check(l,r,k,m-1,psum))
    {
        ans=1LL*(m-1)*(r-l+1)-psum;
        printf("%lld\n",ans/(r-l+1-k));
        return;
    }
    
    int _l=0,_r=m-1,mid;
    while(_l<=_r)
    {
        mid=(_l+_r)>>1;
        if(check(l,r,k,mid,psum))
            _l=mid+1,ans=mid;
        else
            _r=mid-1;
    }
    printf("%lld\n",ans);
}

int main()
{
    freopen("B.in","r",stdin);
    freopen("BBB.out","w",stdout);
    
    scanf("%d%d%d",&n,&m,&q);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&a[i]);
        add(i,a[i]);
        add(i,pii(a[i],i));
    }
    
    while(q--)
    {
        int opt,l,r,k;
        scanf("%d%d%d",&opt,&l,&r);
        
        if(opt==1)
        {
            scanf("%d",&k);
            solve(l,r,k);
        }
        else
        {
            erase(l,pii(a[l],l));
            add(l,pii(r,l));
            add(l,r-a[l]);
            a[l]=r;
        }
    }
    return 0;
}
View Code

 

posted @ 2020-10-26 00:45  LiuRunky  阅读(239)  评论(0编辑  收藏  举报