一些 c++ 模板
写在前面
由于我不会用 allocator
,所以以下内容模板 T
选项的类型需要默认构造函数,如果没有,请补一个没有用的。
因为这里的大部分是手搓代码,不带 STL,又不会 allocator
,所以没有动态内存,有些情况还是请用 STL 吧。
头文件与快读快写,主函数。(以后一定要记得写 cstdio
,标准里 iostream
不包含 cstdio
。)
#include<iostream>
#include<cstdio>
#include<cctype>
#include<vector>
#include<cstring>
#include<algorithm>
using namespace std;
namespace myio {
template<typename T=long long>
T read_int() {
T x=0;char ch,f=1;
while(!std::isdigit(ch=getchar())) if(ch=='-') f=0;
do x=(x<<1)+(x<<3)+ch-'0';
while(std::isdigit(ch=getchar()));
return (f==1?x:-x);
}
template<typename T=long long>
T read_modint(T mod) {
T x=0;char ch,f=1;
while(!std::isdigit(ch=getchar())) if(ch=='-') f=0;
do x=(x*10+ch-'0')%mod;
while(std::isdigit(ch=getchar()));
return (f==1?x:mod-x);
}
template<typename T>
void __print(T x) {
if(x<=9) putchar(x+'0');
else __print(x/10),putchar(x%10+'0');
}
template<typename T>
void print_int(T x,char endch='\n') {
if(x<0) putchar('-'),__print(-x);
else __print(x);
putchar(endch);
}
template<typename T>
void print_int(T x,T y,char sepch=' ',char endch='\n') {
print_int(x,sepch),print_int(y,endch);
}
#if __cplusplus >= 201103L
template<typename T>
void print_int(const std::initializer_list<T> &vec,
char sepch=' ',char endch='\n') {
for(auto items:vec) print_int(items,sepch);
putchar(endch);
}
#endif
template<typename T>
void print_int(std::vector<T> &vec,char sepch=' ',char endch='\n') {
typename std::vector<T>::iterator it=vec.begin();
for(;it!=vec.end();it++) print_int(*it,sepch);
putchar(endch);
}
template<typename T>
void print_int(T* begin,T* end,char sepch=' ',char endch='\n') {
for(T* it=begin;it!=end;it++) myio::print_int(*it,sepch);
putchar(endch);
}
template<typename T>
void print_by_bit(T x,short len=32,char endch='\n') {
for(int i=0;i<len;i++) putchar((x&1)+'0'),x>>=1;
putchar(endch);
}
}
signed main() {
return 0;
}
静态图 Static Graph(需要给 VER_SIZE
和 EDGE_SIZE
赋值,以及定义 EdgeType
):
const int VER_SIZE,EDGE_SIZE;
typedef int EdgeType;
template <typename T> struct span {
T *beg; size_t len;
T* begin() const {return beg;}
T* end() const {return beg+len;}
span(T *_beg,size_t s) {beg=_beg,len=s;}
span() {}
};
vector<EdgeType> _e_buf;
span<EdgeType> e[VER_SIZE+5];
template <typename T> struct edge_array {T edgeData; int nxt;};
edge_array<EdgeType> _edge_init[EDGE_SIZE+5];
int _head[VER_SIZE+5],_tot;
void adde(int _from,EdgeType _edge)
{_edge_init[++_tot].nxt=_head[_from],
_edge_init[_tot].edgeData=_edge;
_head[_from]=_tot;}
void Transform(int _size=VER_SIZE) {
_e_buf.reserve(_size<<1);//这里通常要分配两倍
for(int i=1;i<=_size;i++) {
size_t s=0;
for(int j=_head[i];j!=0;j=_edge_init[j].nxt,s++)
_e_buf.push_back(_edge_init[j].edgeData);
if(s!=0) e[i]=span<EdgeType>(&*(_e_buf.end()-s),s);
}
}
用法:使用 adde(_from,_egde)
加一条从 _from
开始,信息为 _edge
的有向边。加边完成后,Transform(size)
一下,size
可以选择自定义。采用与 vector
存图相同的遍历方式。效率比动态存图高很多。
注意:
EdgeType
至少应该维护这条边的尾,此后应使用形如edge_name.to
的形式访问。当然若EdgeType
是非类的类型,如int
,可以直接用形如edge_name
的形式访问。- 通常来讲,
EDGE_SIZE
要开到VER_SIZE
的两倍,并且size
也得reserve
两倍。 - 此图建立完成后,不可以加新的边。
动态图 Dynamic Graph
std::vector
存图(需要给 VER_SIZE
赋值,需要定义 EdgeType
):
const int VER_SIZE;
typedef int EdgeType;
vector<EdgeType> e[VER_SIZE];
void adde(int _from,EdgeType _edge)
{e[_from].push_back(_edge);}
用法:可以使用 adde()
加边,也可以直接 push_back
。
注意:效率较低。
链式前向星存图这里就省略了。
队列 Queue
template<typename T> class Queue {
private:
T* container;
T temp;
int maxsize,head,tail;
public:
Queue(int _size) {
maxsize=_size,head=1,tail=0;
container=new T[maxsize+1]();
temp=T();
}
void push(T _val) {
if(tail==maxsize) {puts("FULL!!!");exit(114514);}
container[++tail]=_val;
}
void pop_front() {if(head<=tail) head++;}
void pop_back() {if(head<=tail) tail--;}
int& front() {if(tail>=head) return container[head];return temp;}
int& second_front() {if(tail>head)return container[head+1];return temp;}
int size() {return tail-head+1;}
bool empty() {return size()==0;}
int& back() {if(tail>=head) return container[tail];return temp;}
int& second_back() {if(tail>head) return container[tail-1];return temp;}
int& visit(int x)
{if(head+x-1<=tail) return container[head+x-1];return temp;}
void clear() {head=1,tail=0;}
~Queue() {delete []container;}
};
此队列相对 std::queue
的好处是:
- 效率高;
- 可以删除队尾;
- 支持访问队列中任意一个元素(使用
Queue::visit
方法),因此可以实现单调队列; - 方便写斜率优化(雾),因为这个模板是写斜率优化的时候写的。
Queue::clear
效率是 \(O(1)\),可以往复运用空间。
坏处是:
- 需要预分配内存,即:空间复杂度最坏 \(O(n)\),均摊也是 \(O(n)\)。而
std::queue
均摊更小。
树状数组 BIT (Binary Indexed Tree)
首先是三个算子(influence
指的是“如果多计算一次这个元素,答案是否会改变”):
template<typename T> struct addition {
T operator ()(const T &a,const T &b) const
{return a+b;}
const bool influence=true;
};
template<typename T> struct maximum {
T operator ()(const T &a,const T &b) const
{return a<b?b:a;}
const bool influence=false;
};
template<typename T> struct minimum {
T operator ()(const T &a,const T &b) const
{return a<b?a:b;}
const bool influence=false;
};
其次是必须包含的 BITcore
:
template<typename T,typename _operator> class BITcore {
protected:
T *c;
int MAXSIZE;
_operator opt;
BITcore(int _size){
MAXSIZE=_size;
c=new T[MAXSIZE+1]();
}
void UPDATE(int pos,T data)
{for(;pos<=MAXSIZE;pos+=pos&-pos) c[pos]=opt(c[pos],data);}
T QUERY(int pos)
{T ret=T();for(;pos>0;pos-=pos&-pos) ret=opt(ret,c[pos]);return ret;}
~BITcore() {delete []c;}
};
下面是通用的两种 BIT。第一种是 prefix_BIT
,支持三种算子,封装完毕。第二种是 open_BIT
,带有 visit
方法,可以拿来写倍增+树状数组查找:
template <typename T=int,typename _operator=maximum<int> >
struct prefix_BIT : public BITcore<T,_operator> {
prefix_BIT(int _size):BITcore<T,_operator>(_size) {}
prefix_BIT(int _size,T* _init):BITcore<T,_operator>(_size){
if(!this->opt.influence)
for(int i=1;i<=this->MAXSIZE;i++) this->c[i]=_init[i];
for(int i=1;i<=this->MAXSIZE;i++) update(i,_init[i]);
}
void update(int pos,T data) {this->UPDATE(pos,data);}
T query(int pos) {return this->QUERY(pos);}
~prefix_BIT() {}
};
template <typename T=int,typename _operator=maximum<int> >
struct open_BIT : public BITcore<T,_operator> {
open_BIT(int _size):BITcore<T,_operator>(_size) {}
open_BIT(int _size,T* _init):BITcore<T,_operator>(_size){
if(!this->opt.influence)
for(int i=1;i<=this->MAXSIZE;i++) this->c[i]=_init[i];
for(int i=1;i<=this->MAXSIZE;i++) update(i,_init[i]);
}
void update(int pos,T data) {this->UPDATE(pos,data);}
T query(int pos) {return this->QUERY(pos);}
T& visit(int pos) {return this->c[pos];}
~open_BIT() {}
};
最后是两种考试常用的类型:range_query_BIT
和 range_update_BIT
。它们仅支持 addtion
算子:
template <typename T=int>
class range_query_BIT : public BITcore<T,addition<T> > {
private:
void PRETREAT_SOURCE(T* _init) {
for(int i=1;i<=this->MAXSIZE;i++) _init[i]+=_init[i-1];
}
public:
range_query_BIT(int _size):BITcore<T,addition<T> >(_size) {}
range_query_BIT(int _size,T* _init):BITcore<T,addition<T> >(_size){
PRETREAT_SOURCE(_init);
for(int i=1;i<=this->MAXSIZE;i++)
this->c[i]=_init[i]-_init[i-(i&-i)];
}
void update(int pos,T data) {this->UPDATE(pos,data);}
T query(int leftEnd,int rightEnd)
{return this->QUERY(rightEnd)-this->QUERY(leftEnd-1);}
~range_query_BIT() {}
};
template <typename T=int>
struct range_update_BIT : public BITcore<T,addition<T> > {
range_update_BIT(int _size):BITcore<T,addition<T> >(_size) {}
range_update_BIT(int _size,T* _init):BITcore<T,addition<T> >(_size){
for(int i=1;i<=this->MAXSIZE;i++)
this->c[i]=_init[i]-_init[i-(i&-i)];
}
void update(int leftEnd,int rightEnd,T data) {
this->UPDATE(leftEnd,data);
this->UPDATE(rightEnd+1,-data);
}
T query(int pos) {return this->QUERY(pos);}
~range_update_BIT() {}
};
区间查+区间加的树状数组暂时没有意愿写。