一些 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_SIZEEDGE_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 存图相同的遍历方式。效率比动态存图高很多。

注意

  1. EdgeType 至少应该维护这条边的尾,此后应使用形如 edge_name.to 的形式访问。当然若 EdgeType 是非类的类型,如 int,可以直接用形如 edge_name 的形式访问。
  2. 通常来讲,EDGE_SIZE 要开到 VER_SIZE 的两倍,并且 size 也得 reserve 两倍。
  3. 此图建立完成后,不可以加新的边。

动态图 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_BITrange_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() {}
};

区间查+区间加的树状数组暂时没有意愿写。

posted @ 2023-01-02 15:28  robinyqc  阅读(91)  评论(0编辑  收藏  举报