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; }