代码很简单的模板就不收录了。
DFT 离散傅立叶变换
void dft(pdd *a,int l,bool r){
int i,j=l/2,k;
for(i=1;i>=1) j^=k;
j^=k;
}
for(i=1;(1<>1);++k){
pdd v1=a[j+k],v2=a[j+k+(cl>>1)]*cur;
a[j+k]=v1+v2;
a[j+k+(cl>>1)]=v1-v2;
cur=cur*w;
}
}
}
}
hint: 参数 bool r 表示是否在进行逆向的 DFT(即是否是在插值)
hint2: pdd 是手写的 complex 类
NTT 快速数论变换
const LL MOD=998244353,G=3;
const LL MOD2=1004535809,G2=3;
const LL MOD3=65537,G3=3;
void ntt(int *a,int l,bool r){
int i,j=l/2,k;
for(i=1;i>=1) j^=k;
j^=k;
}
for(i=1;(1<>1);++k){
int v1=a[j+k],v2=(LL)a[j+k+(cl>>1)]*(LL)cur%MOD;
a[j+k]=v1+v2;
a[j+k+(cl>>1)]=v1-v2;
if(a[j+k]>=MOD) a[j+k]-=MOD;
if(a[j+k+(cl>>1)]<0) a[j+k+(cl>>1)]+=MOD;
cur=(LL)cur*(LL)w%(LL)MOD;
}
}
}
}
hint: powM(A,B)=\(A^B \bmod MOD\)
后缀自动机与广义后缀自动机
不同于某些无脑写法,这里的GSAM保证不存在等价结点。
struct node{
node *nx[ALPHABET],*fa;
int mxl;
void init(){
mxl=0;
fa=0;
memset(nx,0,sizeof(nx));
}
}*last,*root;
void extend(int v){// for SAM
node *p=last,*np=new node;
np->init();
np->mxl=p->mxl+1;
for(;p && !p->nx[v];p=p->fa) p->nx[v]=np;
if(!p) np->fa=root;
else{
node *q=p->nx[v];
if(q->mxl==p->mxl+1){
np->fa=q;
}
else{
node *nq=new node;
nq->init();
nq->mxl=p->mxl+1;
memcpy(nq->nx,q->nx,sizeof(q->nx));
nq->fa=q->fa;
q->fa=np->fa=nq;
for(;p && p->nx[v]==q;p=p->fa) p->nx[v]=nq;
}
}
last=np;
}
void extend(int c){// for GSAM
node *p=last;
if(p->nx[c]){
if(p->nx[c]->mxl==p->mxl+1)
last=p->nx[c];
else{
node *q=p->nx[c],*nq=new node;
nq->init();
memcpy(nq->nx,q->nx,sizeof(q->nx));
nq->mxl=p->mxl+1;
nq->fa=q->fa;
q->fa=nq;
last=nq;
for(;p && p->nx[c]==q;p=p->fa)
p->nx[c]=nq;
}
return;
}
node *np=new node;
np->init();
np->mxl=p->mxl+1;
for(;p && !(p->nx[c]);p=p->fa)
p->nx[c]=np;
if(!p)
np->fa=root;
else{
node *q=p->nx[c];
if(q->mxl==p->mxl+1)
np->fa=q;
else{
node *nq=new node;
nq->mxl=p->mxl+1;
memcpy(nq->nx,q->nx,sizeof(q->nx));
nq->fa=q->fa;
q->fa=np->fa=nq;
for(;p && p->nx[c]==q;p=p->fa)
p->nx[c]=nq;
}
}
last=np;
}
SA&LCP 后缀数组全家桶
void bsort(){
int i;
memset(cnt,0,sizeof(cnt));
for(i=0;i=0;--i) tmp[--cnt[vy[i]]]=i;
memset(cnt,0,sizeof(cnt));
for(i=0;i=0;--i) ls[--cnt[rk[tmp[i]]]]=tmp[i];
memset(tmp,0,sizeof(tmp));
}
void getsa(){
int i,j,cv;
memset(rk,0,sizeof(rk));
memset(vy,0,sizeof(vy));
for(i=0;i>1)];
}
bsort();
cv=1;
for(j=0;j
hint: vy=value_Y 意为第二排序准则
hint2: rk=rank lv=last_value ls=list
hint3: 可能是因为我的写法太鬼畜了,这段代码里改动几乎任何一处看似无关紧要的细节都会GG……别问我为什么
点分治
void getcen(int ver,int fa,int ts){
int i,j,k,cv=ts-sz[ver];
for(i=0;i<(int)adj[ver].size();++i){
if(adj[ver][i]!=fa && !del[adj[ver][i]]){
UMAX(cv,sz[adj[ver][i]]);
getcen(adj[ver][i],ver,ts);
}
}
UMIN(cur,mpr(cv,ver));
}
void deepin(int ver,int fa){
int i,j,k;
sz[ver]=1;
for(i=0;i<(int)adj[ver].size();++i){
if(!del[adj[ver][i]] && adj[ver][i]!=fa){
deepin(adj[ver][i],ver);
sz[ver]+=sz[adj[ver][i]];
}
}
}
int centroid(int ver){
cur=mpr(iinf,-1);
getcen(ver,-1,sz[ver]);
return cur.second;
}
void solve(int ver){
del[ver]=1;
int i,j,k;
for(i=0;i<(int)adj[ver].size();++i){
if(!del[adj[ver][i]]){
deepin(adj[ver][i],ver);
}
}
for(i=0;i<(int)adj[ver].size();++i){
if(!del[adj[ver][i]]){
solve(centroid(adj[ver][i]));
}
}
}
solve(0);
hint: cv=current_value sz=size
线性基
struct basis{
int arr[35],got[35];
void init(){
memset(arr,0,sizeof(arr));
memset(got,0,sizeof(got));
}
void insert(int v){
int i,j;
for(i=0;i<31;++i){
if(v&(1<=0;--i){
if(v&(1<=0;--j) if(v&(1<
线段树合并与分裂
#define SZ(X) ((X)?((X)->cnt):0)
struct node{
node *lc,*rc;
int cnt;
void init(){lc=rc=0;cnt=0;}
void upd(){cnt=SZ(lc)+SZ(rc);}
bool leaf(){return (!lc && !rc);}
void ins(int d,int l=0,int r=U){
++cnt;
if(l==r) return;
int M=((l+r)>>1);
if(d<=M){
if(!lc){
lc=new node;
lc->init();
}
lc->ins(d,l,M);
}
else{
if(!rc){
rc=new node;
rc->init();
}
rc->ins(d,M+1,r);
}
}
int loc(int id,int l=0,int r=U){
if(l==r) return l;
int M=((l+r)>>1);
if(idloc(id,l,M);
return rc->loc(id-SZ(lc),M+1,r);
}
};
node *merge(node *A,node *B){
if(!A || !B) return (A?A:B);
if(A->leaf() && B->leaf()){
A->cnt+=B->cnt;
delete B;
return A;
}
node *lc=merge(A->lc,B->lc),*rc=merge(A->rc,B->rc);
A->lc=lc;
A->rc=rc;
A->cnt=SZ(lc)+SZ(rc);
delete B;
return A;
}
void split(node *S,int sz,node *&A,node *&B){
if(!S) A=B=0;
else if(S->leaf()){
A=S;
B=new node;
B->init();
B->cnt=S->cnt-sz;
// valuation for B->cnt must be before A->cnt, as A==S is possible.
A->cnt=sz;
}
else if(SZ(S->lc)init();
split(A->rc,sz-SZ(S->lc),A->rc,B->rc);
A->upd();B->upd();
}
else{
B=S;
A=new node;
A->init();
split(B->lc,sz,A->lc,B->lc);
A->upd();B->upd();
}
}
网络流退流(Ford-Fulkerson版本)
namespace flows{
const int S=202,T=203;
vector < pii > adj[205];
int id[205][205],flow;
bool vis[205];
void init(){
memset(id,-1,sizeof(id));
int i;
for(i=0;i<205;++i) adj[i].clear();
flow=0;
}
void addedge(int u,int v,int f){
id[u][v]=(int)adj[u].size();
adj[u].push_back(mpr(v,f));
id[v][u]=(int)adj[v].size();
adj[v].push_back(mpr(u,0));
}
int dfs(int ver,int cur,int tar=T){
int i,j,k;
if(ver==tar || !cur) return cur;
vis[ver]=1;
for(i=0;i<(int)adj[ver].size();++i){
pii &E=adj[ver][i];
if(E.second && !vis[E.first]){
int R=id[E.first][ver],
F=dfs(E.first,MIN(cur,E.second),tar);
if(F){
E.second-=F;
adj[E.first][R].second+=F;
return F;
}
}
}
return 0;
}
void set(){
memset(vis,0,sizeof(vis));
}
void push(){
while(1){
set();
int F=dfs(S,iinf);
if(F) flow+=F; else break;
}
}
void edit(int u,int v,int w){
int d=id[u][v],rd=id[v][u],
f=adj[u][d].second+adj[v][rd].second;
if(adj[v][rd].second<=w){
adj[u][d].second+=w-f;
}
else{
int pf=adj[v][rd].second;
adj[u][d].second=adj[v][rd].second=0;
while(pf>w){
set();
int dlt=dfs(u,pf-w,v);
pf-=dlt;
if(!dlt) break;
}
set();
dfs(T,pf-w,v);
set();
dfs(u,pf-w,S);
flow-=pf-w;
adj[v][rd].second=w;
}
}
};
LCT基本款(之后我大概会在新文章里写一下维护子树信息的LCT?)咕咕咕
namespace LCT{
struct node{
node *s[2],*fa;
int sz,pfa,rev;
void init(){
s[0]=s[1]=fa=0;
pfa=-1;
rev=0;
sz=1;
}
int pos(){
return fa->s[1]==this;
}
void push(){
if(rev){
swap(s[0],s[1]);
if(s[0]) s[0]->rev^=1;
if(s[1]) s[1]->rev^=1;
rev=0;
}
}
void pull(){
sz=SZ(s[0])+SZ(s[1])+1;
}
void rotate(){
if(fa){
node *F=fa;
int ps=pos();
fa=F->fa;
if(fa) fa->s[F->pos()]=this;
pfa=F->pfa;
F->pfa=-1;
F->fa=this;
if(ps){
F->s[1]=s[0];
if(s[0]) s[0]->fa=F;
s[0]=F;
}
else{
F->s[0]=s[1];
if(s[1]) s[1]->fa=F;
s[1]=F;
}
F->pull();
pull();
}
}
void splay(node *tar=0){
while(fa!=tar){
if(fa->fa) fa->fa->push();
fa->push();
push();
if(fa->fa==tar){
rotate();
}
else if(pos()==fa->pos()){
fa->rotate();rotate();
}
else{
rotate();rotate();
}
}
}
}*vers[200005];
void init(){
int i;
for(i=0;i<=n;++i){
vers[i]=new node;
vers[i]->init();
}
}
void expose(int u){
vers[u]->splay();
vers[u]->push();
if(vers[u]->s[1]){
vers[u]->s[1]->pfa=u;
vers[u]->s[1]->fa=0;
vers[u]->s[1]=0;
vers[u]->pull();
}
}
bool extend(int u){
vers[u]->splay();
int &v=vers[u]->pfa;
if(v!=-1){
expose(v);
vers[v]->s[1]=vers[u];
vers[u]->fa=vers[v];
vers[v]->pull();
v=-1;
return 1;
}
return 0;
}
void access(int u){
expose(u);
while(extend(u));
}
void setroot(int u){
access(u);
vers[u]->rev^=1;
}
void link(int u,int v){
setroot(u);
vers[u]->pfa=v;
}
void cut(int u,int v){
setroot(u);
access(v);
vers[v]->push();
vers[v]->s[0]->fa=0;
vers[v]->s[0]=0;
vers[v]->pull();
}
};
可持久化Treap
\(mn\),\(mx\),和\(k\)是节点内维护的一些数据,与模板无关。
#define SZ(x) ((x)?((x)->sz):0)
struct node{
node *s[2];
int mn,mx,sz,k,fix;
void init(int K){
sz=1;
fix=rand();
k=mn=mx=K;
s[0]=s[1]=0;
}
void pull(){
sz=SZ(s[0])+SZ(s[1])+1;
mn=mx=k;
if(s[0]){
mn=min(mn,s[0]->mn);
mx=max(mx,s[0]->mx);
}
if(s[1]){
mn=min(mn,s[1]->mn);
mx=max(mx,s[1]->mx);
}
}
};
void copy(node *S,node *&T){
if(!S) T=0;
T=new node;
*T=*S;
}
void merge(node *&T,node *A,node *B){
if(!A || !B){
copy((A?A:B),T);
return;
}
if(A->fixfix){
copy(A,T);
merge(T->s[1],A->s[1],B);
}
else{
copy(B,T);
merge(T->s[0],A,B->s[0]);
}
T->pull();
}
void splitkey(int K,node *S,node *&A,node *&B){
if(!S){
A=B=0;
return;
}
if(S->ks[1],A->s[1],B);
A->pull();
}
else{
copy(S,B);
splitkey(K,S->s[0],A,B->s[0]);
B->pull();
}
}
动态直线下凸壳
支持\(\mathrm{O}(\log n)\)插入直线,\(\mathrm{O}(\log n)\)单点查询最大值。
常数中等。正确性应该可靠。
const double MOGIC=-1926.0817,eps=1e-8;
struct Line{
LL K,B;
mutable double T;
void set(LL k,LL b,double t=MOGIC){
K=k;B=b;T=t;
}
bool operator <(Line Y) const{
if(T==MOGIC||Y.T==MOGIC) return (K!=Y.K?KY.B);
return T lines;
void ins(Line L){
L.T=MOGIC;
if(lines.empty()){
L.T=(double)-linf;
lines.insert(L);
}
else{
set < Line >::iterator it=lines.lower_bound(L),lt=it,cr;
if(it->K==L.K && it->B==L.B) return;
if(it==lines.begin()){
L.T=(double)-linf;
if(it->K==L.K) lines.erase(lines.begin());
if(!lines.empty()){
it=lines.begin();
it->T=intersect(L,*it);
while((++it)!=lines.end() && lines.begin()->T>it->T+eps){
lines.erase(lines.begin());
it->T=intersect(L,*it);
}
}
lines.insert(L);
return;
}
if((--lt)->K==L.K) return;
if(it==lines.end()){
L.T=intersect(L,*(lines.rbegin()));
while(lines.rbegin()->T>L.T-eps){
lt=lines.end();
lines.erase(--lt);
L.T=intersect(L,*(lines.rbegin()));
}
lines.insert(L);
return;
}
while(1){
cr=it++;
if(it==lines.end()) break;
if(intersect(*lt,L)>intersect(*lt,*cr)-eps || intersect(*it,L)K==L.K){
lines.erase(cr);
while(lt!=lines.begin()){
cr=lt--;
L.T=intersect(*cr,L);
if(L.TT+eps) lines.erase(cr); else break;
}
L.T=intersect(*lines.rbegin(),L);
lines.insert(L);
return;
}
--it;
while(1){
if(lt==lines.begin()) break;
cr=lt--;
if(intersect(*it,L)intersect(*lt,*cr)+eps){
lt=cr;
break;
}
lines.erase(cr);
}
L.T=intersect(*lt,L);
if(L.TT=intersect(*it,L);
lines.insert(L);
}
}
}
LL qry(LL D){
tmp.set(0,0,D);
set < Line > ::iterator it=lines.lower_bound(tmp);
return eval(D,*(--it));
}
};
KM算法
二分图最大权匹配,见UOJ #80
namespace KM{
int n[2],w[505][505];
int matchL[505],matchR[505],pred[505];
int labL[505],labR[505],slk[505];
bool inS[505];
void init(int N){
n[0]=n[1]=N;
memset(w,0,sizeof(w));
memset(matchL,-1,sizeof(matchL));
memset(matchR,-1,sizeof(matchR));
}
LL solve(){
int i,j,k;
memset(labR,0,sizeof(labR));
for(i=0;i0) UMIN(dec,slk[j]);
}
if(dec>0){
for(j=0;j
带花树算法
一般图最大匹配,见UOJ #79
namespace Blossom{
int n,sta[505],fa[505];
int pred[505],match[505];
int tick,mark[505];
int pl,pr,que[1005];
vector < int > adj[505];
void init(int N){
n=N;
int i;
for(i=0;i
ZKW费用流
来源
namespace ZKW{// min cost max flow
const int V=440, E=V*2, maxint=0x3F3F3F3F;
struct etype{
int t, c, u;
etype *next, *pair;
etype() {}
etype(int T, int C, int U, etype* N): t(T), c(C), u(U), next(N) {}
void* operator new(unsigned long long, void* p){return p;}
}*e[V], Te[E+E], *Pe;
int S, T, n, piS, cost;
bool v[V];
void init(int N){
n=N;S=0;T=1;
Pe=Te;
memset(e,0,sizeof(etype*)*(n+2));
cost=piS=0;
}
void addedge(int s, int t, int c, int u){
e[s] = new(Pe++) etype(t, +c, u, e[s]);
e[t] = new(Pe++) etype(s, -c, 0, e[t]);
e[s]->pair = e[t];
e[t]->pair = e[s];
}
int aug(int no, int m){
if (no == T) return cost += piS * m, m;
v[no] = true;
int l = m;
for (etype *i = e[no]; i; i = i->next)
if (i->u && !i->c && !v[i->t]){
int d = aug(i->t, l < i->u ? l : i->u);
i->u -= d, i->pair->u += d, l -= d;
if (!l) return m;
}
return m - l;
}
bool modlabel(){
static int d[V]; memset(d, 0x3F, sizeof(d)); d[T] = 0;
static deque Q; Q.push_back(T);
while(Q.size()){
int dt, no = Q.front(); Q.pop_front();
for(etype *i = e[no]; i; i = i->next)
if(i->pair->u && (dt = d[no] - i->c) < d[i->t])
(d[i->t] = dt) <= d[Q.size() ? Q.front() : 0]
? Q.push_front(i->t) : Q.push_back(i->t);
}
for(int i = 0; i < n; ++i)
for(etype *j = e[i]; j; j = j->next)
j->c += d[j->t] - d[i];
piS += d[S];
return d[S] < maxint;
}
void getcost(){
while(modlabel())
do memset(v, 0, sizeof(int)*(n+2));
while(aug(S, maxint));
}
}
替罪羊式的K-D Tree
题目链接
在平面上支持插入点、查询曼哈顿最近点。
bool dim;
bool cmp(pii A,pii B){
return (dim?A.SC:A.FS)<(dim?B.SC:B.FS);
}
inline int dis(pii A,pii B){
return ABS(A.FS-B.FS)+ABS(A.SC-B.SC);
}
int ans,cnt;
pii ele[600005];
#define SZ(x) ((x)?((x)->sz):0)
struct node{
node *s[2];
pii cen;
int dep,xl,xr,yl,yr,sz;// coordinates of rectangle
bool bad(){
return MAX(SZ(s[0]),SZ(s[1]))*4>sz*3+20;
}
void init(){
s[0]=s[1]=0;
dep=sz=0;
}
void extend(int d){
s[d]=new node;
s[d]->init();
s[d]->xl=xl;s[d]->xr=xr;
s[d]->yl=yl;s[d]->yr=yr;
s[d]->dep=dep+1;
if(!d){
if(dep&1) s[0]->yr=cen.SC;
else s[0]->xr=cen.FS;
}
else{
if(dep&1) s[1]->yl=cen.SC;
else s[1]->xl=cen.FS;
}
}
void build(pii *L,pii *R){
sz=R-L+1;
int M=sz/2,i,j,k;
dim=(dep&1);
nth_element(L,L+M,R+1,cmp);
cen=*(L+M);
if(M){
extend(0);
s[0]->build(L,L+M-1);
}
if(L+Mbuild(L+M+1,R);
}
}
inline int sdis(int L,int R,int X){
return ((X>=L && X<=R)?0:MIN(ABS(X-L),ABS(X-R)));
}
int rdis(pii P){
return sdis(xl,xr,P.FS)+sdis(yl,yr,P.SC);
}
void query(pii P){
UMIN(ans,dis(P,cen));
if(rdis(P)>=ans) return;
node *A=s[0],*B=s[1];
if(B && (!A || A->rdis(P)>B->rdis(P)))
swap(A,B);
if(A) A->query(P);
if(B) B->query(P);
}
void ins(pii P,node *&ls){
++sz;
dim=(dep&1);
int d=cmp(cen,P);
if(!s[d]){
extend(d);
s[d]->cen=P;
s[d]->sz=1;
ls=0;
}
else s[d]->ins(P,ls);
if(bad()) ls=this;
}
void travel(){
if(s[0]) s[0]->travel();
ele[cnt++]=cen;
if(s[1]) s[1]->travel();
}
void del(){
if(s[0]){
s[0]->del();
delete s[0];
s[0]=0;
}
if(s[1]){
s[1]->del();
delete s[1];
s[1]=0;
}
}
}*root;
void rebuild(node *p){
cnt=0;
p->travel();
p->del();
p->build(ele,ele+cnt-1);
}
void insert(pii P){
node *r=0;
root->ins(P,r);
if(r) rebuild(r);
}
int query(pii P){
ans=iinf;
root->query(P);
return ans;
}
广义圆方树建树
对于点双建立方点。
int en[200005],low[200005],tick,cnt;// initially cnt set to |V|, en set to -1
int sz,stk[200005],fa[400005];
vector < int > son[400005],adj[200005];
void setfa(int s,int f){
fa[s]=f;
son[f].push_back(s);
}
void tarjan(int v,int f){
int i,j,k;
stk[sz++]=v;
en[v]=low[v]=tick++;
for(i=0;i<(int)adj[v].size();++i){
int u=adj[v][i];
if(u!=f){
if(en[u]==-1){
tarjan(u,v);
UMIN(low[v],low[u]);
if(low[u]==en[v]){
int cen=cnt++;
setfa(cen,v);
while(sz && en[stk[sz-1]]>=en[u]){
setfa(stk[sz-1],cen);
--sz;
}
}
else if(low[u]==en[u]){
int cen=cnt++;
setfa(u,cen);
setfa(cen,v);
assert(sz && stk[sz-1]==u);
--sz;
}
}
else UMIN(low[v],en[u]);
}
}
}