模板集合
Basic
通用模板
#include <bits/stdc++.h>
using namespace std;
using db=double;
using i64=long long;
using vi=vector<int>;
using pii=pair<int,int>;
template<typename T>
inline T read(){
T x=0,f=0;char ch=getchar();
while(!isdigit(ch)) f|=(ch=='-'),ch=getchar();
while(isdigit(ch)) x=x*10+(ch^48),ch=getchar();
return f?-x:x;
}
#define rdi read<int>
#define rdi64 read<i64>
#define fi first
#define se second
#define pb push_back
#define mp make_pair
int main(){
#ifdef LOCAL
freopen("1.in","r",stdin);
freopen("1.out","w",stdout);
#endif
return 0;
}
读入优化
namespace GTR {
const int bufl = 1 << 15;
char buf[bufl], *s = buf, *t = buf;
inline int fetch() {
if (s == t) { t = (s = buf) + fread(buf, 1, bufl, stdin); if (s == t) return EOF; }
return *s++;
}
inline int read() {
int a = 0, b = 1, c = fetch();
while (c < 48 || c > 57) b ^= c == '-', c = fetch();
while (c >= 48 && c <= 57) a = (a << 1) + (a << 3) + c - 48, c = fetch();
return b ? a : -a;
}
} using GTR::read;
DS
Segment Tree Beats
区间取 min,区间加,区间求和。
struct Node{
ll mx,smx,sum;
int cnt;
friend Node operator + (Node a,Node b){
if(!a.cnt) return b;
if(!b.cnt) return a;
if(a.mx<b.mx) swap(a,b);
if(a.mx>b.mx) return {a.mx,max(a.smx,b.mx),a.sum+b.sum,a.cnt};
else return {a.mx,max(a.smx,b.smx),a.sum+b.sum,a.cnt+b.cnt};
}
}t[N*4];
ll tmx[N*4],tsum[N*4];
void pushup(int now) {t[now]=t[lson]+t[rson];}
void upd1(int now,ll t1){
if(t[now].mx<t1) return;
t[now].sum-=(t[now].mx-t1)*t[now].cnt,t[now].mx=tmx[now]=t1;
}
void upd2(int now,ll t2){
t[now].mx+=t2,t[now].smx+=t2,t[now].sum+=t[now].cnt1*t2,tsum[now]+=t2,tmx[now]+=t2;
}
void pushdown(int now){
if(tsum[now]) upd2(lson,tsum[now]),upd2(rson,tsum[now]),tsum[now]=0;
if(tmx[now]!=INF) upd1(lson,tmx[now]),upd1(rson,tmx[now]),tmx[now]=INF;
}
void add(int now,int l,int r,int x,int y,ll val){
//与普通线段树相同
//调用 upd2
}
void chkmin(int now,int l,int r,int x,int y,ll val){
if(x>y||val>=t[now].mx) return;
if(x<=l&&r<=y&&val>t[now].smx) return upd1(now,val);
if(l==r) {t[now].mx=min(t[now].mx,val),t[now].sum=t[now].mx;return;}
pushdown(now);
if(x<=mid) chkmin(lson,l,mid,x,y,val);
if(y>mid) chkmin(rson,mid+1,r,x,y,val);
pushup(now);
}
void query(){
//与普通线段树相同
}
李超树
支持插入直线,查询,合并。
struct Line{
ll k,b;
ll operator ()(ll x) const{return k*x+b;}
};
struct SGT{
#define lson (t[now].ls)
#define rson (t[now].rs)
#define mid ((l+r)>>1)
struct Node{Line cur;int ls,rs;}t[N*60];
int tot;
int newnode(){
t[++tot]={{0,INFl},0,0};
return tot;
}
void insert(int &now,int l,int r,Line x){
if(!now) now=newnode();
if(t[now].cur(mid)>x(mid)) swap(t[now].cur,x);
if(l==r) return;
if(x(l)<t[now].cur(l)) insert(lson,l,mid,x);
if(x(r)<t[now].cur(r)) insert(rson,mid+1,r,x);
}
ll query(int now,int l,int r,int x){
if(!now) return INFl;
ll ret=t[now].cur(x);
if(l==r) return ret;
ret=min(ret,(x<=mid?query(lson,l,mid,x):query(rson,mid+1,r,x)));
return ret;
}
void merge(int &now1,int now2,int l,int r){
if(!now1||!now2) {now1|=now2;return;}
if(l<r){
merge(t[now1].ls,t[now2].ls,l,mid);
merge(t[now1].rs,t[now2].rs,mid+1,r);
}
insert(now1,l,r,t[now2].cur);
}
#undef lson
#undef rson
#undef mid
}t;
支持插入线段,区间查询。
struct SGT{
struct Line{
i64 k,b;
i64 operator ()(i64 x) {return x*k+b;}
};
#define lson (t[now].ls)
#define rson (t[now].rs)
#define mid ((l+r)>>1)
struct Node{Line p;i64 mi;int ls,rs;}t[N*30];
int tot,rt;
SGT(){t[tot=rt=0].mi=INF;}
int new_n() {t[++tot]={{0,INF},INF,0,0};return tot;}
void pushup(int now,int l,int r){
t[now].mi=min({t[lson].mi,t[rson].mi,t[now].p(l),t[now].p(r)});
}
void insert(int &now,int l,int r,int x,int y,Line p){
if(!now) now=new_n();
if(x<=l&&r<=y){
if(t[now].p(mid)>p(mid)) swap(t[now].p,p);
if(l==r) {t[now].mi=t[now].p(l);return;}
if(p(l)<t[now].p(l)) insert(lson,l,mid,x,y,p);
if(p(r)<t[now].p(r)) insert(rson,mid+1,r,x,y,p);
pushup(now,l,r);return;
}
if(x<=mid) insert(lson,l,mid,x,y,p);
if(y>mid) insert(rson,mid+1,r,x,y,p);
pushup(now,l,r);
}
i64 query(int now,int l,int r,int x,int y){
if(!now) return INF;
if(x<=l&&r<=y) return t[now].mi;
i64 ret=min(t[now].p(max(x,l)),t[now].p(min(y,r)));
if(x<=mid) ret=min(ret,query(lson,l,mid,x,y));
if(y>mid) ret=min(ret,query(rson,mid+1,r,x,y));
return ret;
}
#undef lson
#undef rson
#undef mid
}t;
历史最值线段树
支持区间加减,单点修改(区间赋值以后补)。
struct SGT{
#define lson (now<<1)
#define rson (now<<1|1)
#define mid ((l+r)>>1)
struct Node{ll mx,hmx,ad,h_ad;}t[N*4];
void upd(int now,ll ad,ll h_ad){
t[now].hmx=max(t[now].mx+h_ad,t[now].hmx),t[now].mx+=ad;
t[now].h_ad=max(t[now].h_ad,t[now].ad+h_ad),t[now].ad+=ad;
}
void pushdown(int now){
if(t[now].ad||t[now].h_ad){
upd(lson,t[now].ad,t[now].h_ad),upd(rson,t[now].ad,t[now].h_ad);
t[now].ad=t[now].h_ad=0;
}
}
void pushup(int now){
t[now].mx=max(t[lson].mx,t[rson].mx);
t[now].hmx=max({t[now].mx,t[lson].hmx,t[rson].hmx});
}
void build(int now,int l,int r){
t[now].mx=t[now].hmx=-INFl;
if(l==r) return;
build(lson,l,mid);build(rson,mid+1,r);
}
void add(int now,int l,int r,int x,int y,ll val){
if(x<=l&&r<=y) {upd(now,val,max(val,0ll));return;}
pushdown(now);
if(x<=mid) add(lson,l,mid,x,y,val);
if(y>mid) add(rson,mid+1,r,x,y,val);
pushup(now);
}
void set(int now,int l,int r,int x,ll val){
if(l==r){
t[now].mx=val;
t[now].hmx=max(t[now].hmx,t[now].mx);
return;
}
pushdown(now);
x<=mid?set(lson,l,mid,x,val):set(rson,mid+1,r,x,val);
pushup(now);
}
ll query(int now,int l,int r,int x,int y){
if(x<=l&&r<=y) return t[now].hmx;
ll ret=-INFl;pushdown(now);
if(x<=mid) ret=max(ret,query(lson,l,mid,x,y));
if(y>mid) ret=max(ret,query(rson,mid+1,r,x,y));
return ret;
}
#undef lson
#undef rson
#undef mid
};
K-D Tree
单点加,矩形查。
using point=array<int,2>;
template<size_t N>
struct kdtree{
#define lson (t[now].ls)
#define rson (t[now].rs)
struct Data{point p;i64 val;}buf[N];
struct Node{
int ls,rs,siz;i64 sum;
point mi,mx;Data cur;
}t[N];
int fre[N],tp,tot,rt;
inline int new_n() {return tp?fre[tp--]:++tot;}
void pushup(int now){
t[now].siz=t[lson].siz+t[rson].siz+1;
t[now].sum=t[lson].sum+t[rson].sum+t[now].cur.val;
t[now].mi=t[now].mx=t[now].cur.p;
for(int i=0;i<2;i++)
for(auto son:{lson,rson})
if(son) t[now].mi[i]=min(t[now].mi[i],t[son].mi[i]),
t[now].mx[i]=max(t[now].mx[i],t[son].mx[i]);
}
void build(int &now,Data *st,Data *ed,int di){
if(st==ed) {now=0;return;}
Data *mid=st+(ed-st)/2;
nth_element(st,mid,ed,[&](const Data &a,const Data &b){return a.p[di]<b.p[di];});
t[now=new_n()].cur=*mid,t[now].sum=mid->val;
build(lson,st,mid,di^1),build(rson,mid+1,ed,di^1);
pushup(now);
}
void print(int now,Data *res){
if(!now) return;
print(lson,res);
fre[++tp]=now,res[t[lson].siz]=t[now].cur;
print(rson,res+t[lson].siz+1);
}
void rebuild(int &now,int di){
print(now,buf);
build(now,buf,buf+t[now].siz,di);
}
void check(int &now,int di){
static const db A=0.75;
db lim=t[now].siz*A;
if(max(t[lson].siz,t[rson].siz)>lim) rebuild(now,di);
}
void insert(int &now,point p,i64 v,int di=0){
if(!now){
t[now=new_n()].siz=1;
t[now].mi=t[now].mx=t[now].cur.p=p;
t[now].sum=t[now].cur.val=v;
return;
}
if(p[di]<=t[now].cur.p[di]) insert(lson,p,v,di^1);
else insert(rson,p,v,di^1);
pushup(now);check(now,di);
}
i64 query(int now,point p1,point p2,int di=0){
if(!now||t[now].mx[0]<p1[0]||p2[0]<t[now].mi[0]||
t[now].mx[1]<p1[1]||p2[1]<t[now].mi[1]) return 0;
if(p1[0]<=t[now].mi[0]&&t[now].mx[0]<=p2[0]&&
p1[1]<=t[now].mi[1]&&t[now].mx[1]<=p2[1]) return t[now].sum;
i64 ret=0;
if(p1[0]<=t[now].cur.p[0]&&t[now].cur.p[0]<=p2[0]&&
p1[1]<=t[now].cur.p[1]&&t[now].cur.p[1]<=p2[1]) ret+=t[now].cur.val;
ret+=query(lson,p1,p2,di^1)+query(rson,p1,p2,di^1);
return ret;
}
#undef lson
#undef rson
};
Geometry
向量类
struct Vec{
double x,y;
friend bool operator == (Vec a,Vec b){return a.x==b.x&&a.y==b.y;}
double abs() const{return hypot(x,y);}
double deg() const{return atan2(y,x);}
};
Vec operator + (const Vec &a,const Vec &b) {return {a.x+b.x,a.y+b.y};}
Vec operator - (const Vec &a,const Vec &b) {return {a.x-b.x,a.y-b.y};}
double dot(const Vec &a,const Vec &b) {return a.x*b.x+a.y*b.y;}
double cross(const Vec &a,const Vec &b) {return a.x*b.y-a.y*b.x;}
凸包
vector<Vec> convex(vector<Vec> v){
int cnt=v.size();
for(int i=1;i<cnt;i++)
if(v[i].y<v[0].y||(v[i].y==v[0].y&&v[i].x<v[0].x)) swap(v[i],v[0]);
sort(v.begin()+1,v.end(),[&](Vec a,Vec b){
double val=cross(a-v[0],b-v[0]);
return fabs(val)<eps?mp(a.x,a.y)<mp(b.x,b.y):val>0;
});
static int st[N],tp;
st[tp=1]=0;
for(int i=1;i<cnt;i++){
while(tp>1&&cross(v[i]-v[st[tp-1]],v[st[tp]]-v[st[tp-1]])>=0) --tp;
st[++tp]=i;
}
vector<Vec> ret(tp);
for(int i=0;i<tp;i++) ret[i]=v[st[i+1]];
return ret;
}
多边形面积
double area(const vector<Vec> &a){
int siz=a.size();
double sum=0;
for(int i=0;i<siz;i++){
int nxt=(i+1>=siz?i+1-siz:i+1);
sum+=cross(a[i],a[nxt]);
}
return abs(sum/2);
}
Graph
欧拉路/回路
只有定向,没有输出路径。
struct Edge{int to,nxt,fx;}e[N];
int head[N],tot=1,vis[N];
void add_e(int x,int y){
e[++tot]={y,head[x],0};
head[x]=tot;
}
void euler(int x){
for(int &i=head[x];i;i=e[i].nxt){
if(vis[i>>1]) continue;
vis[i>>1]=1,e[i].fx=1;
euler(e[i].to);
}
}
String
Suffix Array
来自 OI-wiki。
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int N = 1000010;
char s[N];
int n, sa[N], rk[N], oldrk[N << 1], id[N], px[N], cnt[N];
// px[i] = rk[id[i]](用于排序的数组所以叫 px)
bool cmp(int x, int y, int w) {
return oldrk[x] == oldrk[y] && oldrk[x + w] == oldrk[y + w];
}
int main() {
int i, m = 300, p, w;
scanf("%s", s + 1);
n = strlen(s + 1);
for (i = 1; i <= n; ++i)
++cnt[rk[i] = s[i]];
for (i = 1; i <= m; ++i)
cnt[i] += cnt[i - 1];
for (i = n; i >= 1; --i)
sa[cnt[rk[i]]--] = i;
for (w = 1;; w <<= 1, m = p) { // m=p 就是优化计数排序值域
for (p = 0, i = n; i > n - w; --i)
id[++p] = i;
for (i = 1; i <= n; ++i)
if (sa[i] > w)
id[++p] = sa[i] - w;
memset(cnt, 0, sizeof(cnt));
for (i = 1; i <= n; ++i)
++cnt[px[i] = rk[id[i]]];
for (i = 1; i <= m; ++i)
cnt[i] += cnt[i - 1];
for (i = n; i >= 1; --i)
sa[cnt[px[i]]--] = id[i];
memcpy(oldrk, rk, sizeof(rk));
for (p = 0, i = 1; i <= n; ++i)
rk[sa[i]] = cmp(sa[i], sa[i - 1], w) ? p : ++p;
if (p == n) {
for (int i = 1; i <= n; ++i)
sa[rk[i]] = i;
break;
}
}
for (i = 1; i <= n; ++i)
printf("%d ", sa[i]);
return 0;
}
\(\mathit{height}\) 数组
来自 OI-wiki。
for (i = 1, k = 0; i <= n; ++i) {
if (rk[i] == 1) continue;
if (k) --k;
while (s[i + k] == s[sa[rk[i] - 1] + k]) ++k;
height[rk[i]] = k;
}