代码很简单的模板就不收录了。

DFT 离散傅立叶变换

pdd curv[262144];
void dft(pdd *a,int l,bool r){
int i,j=l/2,k;
for(i=1;i<l;++i){
if(i<j) swap(a[i],a[j]);
for(k=l/2;j&k;k>>=1) j^=k;
j^=k;
}
for(i=1;(1<<i)<=l;++i){
int cl=(1<<i);
pdd w=mpr(cos(2.0*pi/(double)cl),sin(2.0*pi/(double)cl));
if(r) w.second=-w.second;
for(j=0;j<l;j+=cl){
pdd cur=mpr(1.0,0.0);
for(k=0;k<(cl>>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;
if(!j) curv[k]=cur*w;
cur=curv[k];
}
}
}
}

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;
int curv[262144];
void ntt(int *a,int l,bool r){
int i,j=l/2,k;
for(i=1;i<l;++i){
if(i<j) swap(a[i],a[j]);
for(k=l/2;j&k;k>>=1) j^=k;
j^=k;
}
for(i=1;(1<<i)<=l;++i){
int cl=(1<<i);
int w=powM(G,(MOD-1)/cl);
if(r) w=powM(w,MOD-2);
for(j=0;j<l;j+=cl){
int cur=1,T=(cl>>1)+j;
for(k=0;k<(cl>>1);++k){
int v1=a[j+k],v2=(LL)a[T+k]*(LL)cur%MOD;
a[j+k]+=v2;
if(a[j+k]>=MOD) a[j+k]-=MOD;
a[T+k]=v1-v2;
if(a[T+k]<0) a[T+k]+=MOD;
if(!j) curv[k]=(LL)cur*w%MOD;
cur=curv[k];
}
}
}
}

hint: powM(A,B)=\(A^B \bmod MOD\)

hint2: 这个DFT板子单次变换耗时约为NTT单次变换耗时的3/2

多项式求逆

注意:该模板及以下所有多项式模板中,参数\(n\)均指\(deg+1\)而非\(deg\)。

int A[262144],B[262144];
void polyinv(int *p,int *f,int n){
int i,j,k,tl=2,llen=0;
while(tl<n) tl<<=1;
memset(f,0,sizeof(int)*tl);
f[0]=powM(p[0]);
for(i=1;i<tl;i<<=1){
int ni=(i<<1);
memset(A,0,sizeof(int)*(ni<<1));
memset(B,0,sizeof(int)*(ni<<1));
for(j=0;j<ni && j<n;++j) A[j]=p[j];
for(j=0;j<i;++j) B[j]=f[j];
ntt(A,ni<<1,0);
ntt(B,ni<<1,0);
for(j=0;j<(ni<<1);++j){
f[j]=B[j]+B[j]-(LL)B[j]*(LL)B[j]%MOD*(LL)A[j]%MOD;
if(f[j]>=MOD) f[j]-=MOD;
if(f[j]<0) f[j]+=MOD;
}
ntt(f,ni<<1,1);
int iv=powM(ni<<1);
for(j=0;j<ni;++j) f[j]=(LL)f[j]*(LL)iv%MOD;
llen=(ni<<1);
}
memset(f+n,0,sizeof(int)*(llen-n));
}

hint: 数组大小应开到4n(其中n为模数中x的次数),这是中间结果的最大次数。

hint2: 传入的p和f必须是不同的数组

多项式ln

int tmp[524288];
void polyln(int *p,int *f,int n){
int i,j,k,len=4;
while(len<=n*2) len<<=1;
memset(f,0,sizeof(int)*len);
for(i=1;i<n;++i){
f[i-1]=(LL)i*(LL)p[i]%MOD;
}
polyinv(p,tmp,n);
ntt(f,len,0);
ntt(tmp,len,0);
for(i=0;i<len;++i){
f[i]=(LL)f[i]*(LL)tmp[i]%MOD;
}
ntt(f,len,1);
for(i=n-1;i>0;--i){
f[i]=(LL)f[i-1]*(LL)powM((LL)i*(LL)len%MOD)%MOD;
}
f[0]=0;
memset(f+n,0,sizeof(int)*(len-n));
}

hint: 数组大小应开到4n(其中n为模数中x的次数),这是中间结果的最大次数。

hint2: 传入的p和f必须是不同的数组

hint3: 传入的多项式p的常数项必须=1

多项式exp

int tmpA[524288],tmpB[524288];
void polyexp_solve(int *p,int *f,int l,int r){
if(l==r) return;
int M=(l+r)/2,i;
polyexp_solve(p,f,l,M);
int len=2;
while(len<(r-l+1)*2) len<<=1;
memset(tmpA,0,sizeof(int)*len);
for(i=l;i<=M;++i){
tmpA[i-l]=f[i];
}
ntt(tmpA,len,0);
memset(tmpB,0,sizeof(int)*len);
for(i=l;i<=r;++i){
tmpB[i-l]=p[i-l];
}
ntt(tmpB,len,0);
for(i=0;i<len;++i){
tmpB[i]=(LL)tmpA[i]*(LL)tmpB[i]%MOD;
}
ntt(tmpB,len,1);
for(i=M;i<r;++i){
f[i+1]+=(LL)tmpB[i-l]*(LL)powM((LL)(i+1)*(LL)len%MOD)%MOD;
if(f[i+1]>=MOD) f[i+1]-=MOD;
}
polyexp_solve(p,f,M+1,r);
}
void polyexp(int *p,int *f,int n){
int i,j,k;
for(i=1;i<n;++i){
p[i-1]=(LL)p[i]*(LL)i%MOD;
}
p[n-1]=0;
memset(f,0,sizeof(int)*n);
f[0]=1;
polyexp_solve(p,f,0,n-1);
}

hint: 数组大小应开到4n(其中n为模数中x的次数),这是中间结果的最大次数。

hint2: 传入的p和f必须是不同的数组

hint3: 传入的多项式p的常数项必须=0

多项式开平方

int tA[524288],tB[524288];
void polysqrt(int *p,int *f,int n){
int i,j,k,tl=2,llen=0;
while(tl<n) tl<<=1;
memset(f,0,sizeof(int)*tl);
f[0]=1;
for(i=1;i<tl;i<<=1){
int ni=(i<<1);
memset(tA,0,sizeof(int)*(ni<<1));
memset(tB,0,sizeof(int)*(ni<<1));
for(j=0;j<ni && j<n;++j) tA[j]=p[j];
polyinv(f,tB,ni);
ntt(tA,ni<<1,0);
ntt(tB,ni<<1,0);
for(j=0;j<(ni<<1);++j){
tB[j]=(LL)tB[j]*(LL)tA[j]%MOD;
}
ntt(tB,ni<<1,1);
int iv=powM(ni<<1),inv2=(MOD+1)/2;
for(j=0;j<ni;++j){
f[j]+=(LL)tB[j]*(LL)iv%MOD;
f[j]=(LL)f[j]*(LL)inv2%MOD;
}
llen=(ni<<1);
}
memset(f+n,0,sizeof(int)*(llen-n));
}

hint: 数组大小应开到4n(其中n为模数中x的次数),这是中间结果的最大次数。

hint2: 传入的p和f必须是不同的数组

hint3: 传入的多项式p的常数项必须=1

后缀自动机与广义后缀自动机

不同于某些无脑写法,这里的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->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[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<n;++i) ++cnt[vy[i]];
for(i=1;i<=n+3 || i<=30;++i) cnt[i]+=cnt[i-1];
for(i=n-1;i>=0;--i) tmp[--cnt[vy[i]]]=i;
memset(cnt,0,sizeof(cnt));
for(i=0;i<n;++i) ++cnt[rk[tmp[i]]];
for(i=1;i<=n+3 || i<=30;++i) cnt[i]+=cnt[i-1];
for(i=n-1;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<n;++i) {
rk[i]=s[i]-'a'+1;
}
for(i=2;i<=n*2;i<<=1){
for(j=0;j<n;++j){
vy[j]=rk[j+(i>>1)];
}
bsort();
cv=1;
for(j=0;j<n;++j){
if(j && (rk[ls[j-1]]!=rk[ls[j]]||vy[ls[j-1]]!=vy[ls[j]]))
++cv;
tmp[ls[j]]=cv;
}
memcpy(rk,tmp,sizeof(tmp));
}
}
void getlcp(){
int i,j,lv=0;
for(i=0;i<n;++i){
if(rk[i]==1) continue;
int la=ls[rk[i]-2];
for(j=MAX(0,lv-2);la+j<n&&i+j<n;++j){
if(s[la+j]!=s[i+j]) break;
}
lcp[i]=lv=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<<i)) got[i]=1;
}
for(i=30;i>=0;--i){
if(v&(1<<i)){
if(arr[i])
v^=arr[i];
else{
for(j=i-1;j>=0;--j) if(v&(1<<j)) v^=arr[j];
for(j=i+1;j<31;++j) if(arr[j]&(1<<i)) arr[j]^=v;
arr[i]=v;
}
}
}
}
};

线段树合并与分裂

#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(id<SZ(lc)) return lc->loc(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)<sz){
A=S;
B=new node;
B->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->fix<B->fix){
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->k<K){
copy(S,A);
splitkey(K,S->s[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?K<Y.K:B>Y.B);
return T<Y.T;
}
}tmp;
LL eval(LL d,Line line){
return d*line.K+line.B;
}
double intersect(Line A,Line B){
return (double)(A.B-B.B)/(double)(B.K-A.K);
}
struct convex{
set < Line > 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)<intersect(*it,*cr)-eps)
break;
lines.erase(cr);
}
if(cr->K==L.K){
lines.erase(cr);
while(lt!=lines.begin()){
cr=lt--;
L.T=intersect(*cr,L);
if(L.T<lines.rbegin()->T+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(*it,*cr)+eps || intersect(*lt,L)>intersect(*lt,*cr)+eps){
lt=cr;
break;
}
lines.erase(cr);
}
L.T=intersect(*lt,L);
if(L.T<intersect(*it,L)){
it->T=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;i<n[0];++i){
labL[i]=0;
for(j=0;j<n[1];++j) UMAX(labL[i],w[i][j]);
}
for(i=0;i<n[0];++i){
if(matchL[i]!=-1) continue;
memset(inS,0,sizeof(inS));
inS[i]=1;
for(j=0;j<n[1];++j){
slk[j]=labL[i]+labR[j]-w[i][j];
}
int dec=0;
while(dec!=-1){
dec=-2;
while(dec==-2){
dec=iinf;
for(j=0;j<n[1];++j){
if(!slk[j]){
for(k=0;k<n[0];++k){
if(inS[k] && w[k][j]==labL[k]+labR[j]) break;
}
pred[j]=k;
slk[j]=-1;
int v=matchR[j];
if(v==-1){
for(k=0;j!=-1;j=k){
k=matchL[pred[j]];
matchR[j]=pred[j];
matchL[pred[j]]=j;
}
dec=-1;
}
else{
inS[v]=1;
for(k=0;k<n[1];++k){
UMIN(slk[k],labL[v]+labR[k]-w[v][k]);
}
dec=-2;
}
break;
}
if(slk[j]>0) UMIN(dec,slk[j]);
}
if(dec>0){
for(j=0;j<n[0];++j){
if(inS[j]) labL[j]-=dec;
}
for(j=0;j<n[1];++j){
if(slk[j]!=-1)
slk[j]-=dec;
else
labR[j]+=dec;
}
}
}
}
}
LL res=0ll;
for(i=0;i<n[0];++i) res+=(LL)w[i][matchL[i]];
return res;
}
};

带花树算法

一般图最大匹配,见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<n;++i) adj[i].clear();
memset(mark,0,sizeof(mark));
memset(match,-1,sizeof(match));
tick=0;
}
int ancestor(int x){
return (fa[x]==x?x:(fa[x]=ancestor(fa[x])));
}
int lca(int u,int v){
++tick;
while(u==-1 || mark[u]!=tick){
if(u!=-1){
mark[u]=tick;
u=(match[u]==-1?-1:ancestor(pred[match[u]]));
}
swap(u,v);
}
return u;
}
void blossom(int u,int v,int L){
while(ancestor(v)!=L){
pred[v]=u;
u=match[v];
if(sta[u]==2){
sta[u]=1;
que[pr++]=u;
}
fa[u]=fa[v]=L;
v=pred[u];
}
}
int solve(){
int i,j,k,res=0;
for(i=0;i<n;++i){
if(match[i]!=-1) continue;
for(j=0;j<n;++j) fa[j]=j;
memset(sta,0,sizeof(sta));
pl=0;pr=1;
que[0]=i;
sta[i]=1;
while(pl<pr && match[i]==-1){
int v=que[pl++];
for(j=0;j<(int)adj[v].size();++j){
int u=adj[v][j];
if(!sta[u]){
pred[u]=v;
if(match[u]==-1){
int nx=0;
for(k=u;k!=-1;k=nx){
nx=match[pred[k]];
match[k]=pred[k];
match[pred[k]]=k;
}
++res;
break;
}
else{
sta[u]=2;
sta[match[u]]=1;
que[pr++]=match[u];
}
}
else if(sta[u]==1 && ancestor(u)!=ancestor(v)){
int LCA=lca(u,v);
blossom(u,v,LCA);
blossom(v,u,LCA);
}
}
}
}
return res;
}
};

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<int> 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+M<R){
extend(1);
s[1]->build(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]);
}
}
}

支持DecreaseKey的配对堆

struct ph{// min-heap
ph *son,*sib,*fa;
pli key;
void init(pli dat){
key=dat;
son=sib=fa=0;
}
};
ph *merge(ph *A,ph *B){
if(!A || !B) return A?A:B;
A->sib=B->sib=A->fa=B->fa=0;
if(A->key>B->key) swap(A,B);
B->sib=A->son;
if(A->son) A->son->fa=B;
B->fa=A;
A->son=B;
return A;
}
ph *mergesibs(ph *A){
if(!A) return 0;
if(!A->sib){
A->fa=0;
return A;
}
ph *O=A->sib->sib;
if(!O) return merge(A,A->sib);
return merge(merge(A,A->sib),mergesibs(O));
}
ph *deckey(ph *A,ph *tar,LL dlt){
tar->key.FS-=dlt;
ph *F=tar->fa;
if(!F) return A;
ph *S=tar->sib;
(F->son==tar?F->son:(F->sib))=S;
if(S) S->fa=F;
return merge(A,tar);
}
ph *popmin(ph *A){
return mergesibs(A->son);
}

BM算法求解最短线性递推式

int n,a[3005],cnt,fail[3005],dlt[3005];
vector < int > coe[3005];
int delta(vector < int > &coes,int p){
if(p<(int)coes.size()) return 0;
int r=a[p],i;
for(i=0;i<(int)coes.size();++i){
r-=(LL)coes[i]*(LL)a[p-i-1]%MOD;
if(r<0) r+=MOD;
}
return r;
}
vector < int > BM(){// get the shortest recurrence fomula from array a
int i,j,k;
vector < int > cur;
coe[cnt]=cur;
int ptr=0;
int d=-1,v=-1;
while(1){
while(ptr<n){
dlt[cnt]=delta(cur,ptr);
fail[cnt]=ptr;
if(!dlt[cnt])
++ptr;
else break;
}
if(ptr==n) break;
if(!cnt){
cur.resize(fail[cnt]+1,0);
coe[++cnt]=cur;
}
else{
int prt1=fail[cnt]-fail[d]-1,prt3=(int)coe[d].size();
if((int)cur.size()<prt1+prt3+1)
cur.resize(prt1+prt3+1,0);
int mul=(LL)dlt[cnt]*(LL)powM(dlt[d])%MOD;
cur[prt1]+=mul;
cur[prt1]%=MOD;
for(i=0;i<prt3;++i){
int &T=cur[prt1+1+i];
T-=(LL)mul*(LL)coe[d][i]%MOD;
if(T<0) T+=MOD;
}
coe[++cnt]=cur;
}
if(fail[cnt-1]-(int)coe[cnt-1].size()>v){
v=fail[cnt-1]-(int)coe[cnt-1].size();
d=cnt-1;
}
}
return cur;
}

类欧几里得算法

题目链接

LL pws(int n,int k){
if(!k) return (LL)n%MOD;
if(k==1) return (LL)n*(LL)(n+1)/2ll%MOD;
if(k==2) return (LL)n*(LL)(n+1)%MOD*(LL)(2*n+1)%MOD*inv6%MOD;
}
void calc(int a,int b,int c,int n,LL &f,LL &g,LL &h){// a,c,n>=0
if(!a){
int nn=max(0,b/c);
f=(LL)nn*(LL)n%MOD;
g=(LL)nn*pws(n,1)%MOD;
h=pws(nn,1)*(LL)n%MOD;
}
else if(b<0){
int mv=(-b+a-1)/a,nb=b+mv*a;
if(n<=mv)
f=g=h=0ll;
else{
calc(a,nb,c,n-mv,f,g,h);
(g+=f*mv)%=MOD;
}
if(n>=mv){
(f+=(LL)(nb/c))%=MOD;
(g+=(LL)mv*(LL)(nb/c))%=MOD;
(h+=pws(nb/c,1))%=MOD;
}
}
else if(a>=c){
LL da=a/c;
LL df=da*pws(n,1);
LL dg=da*pws(n,2);
LL dh=da*inv2%MOD*pws(n,1)-da*da%MOD*inv2%MOD*pws(n,2);// +da*g reserved
calc(a-c*da,b,c,n,f,g,h);
(f+=df)%=MOD;
(g+=dg)%=MOD;
(h+=dh+g*da)%=MOD;
}
else{
int nn=((LL)a*(LL)n+(LL)b)/(LL)c;
calc(c,-b-1,a,nn,f,g,h);
f=((LL)n*(LL)nn-f)%MOD;
LL ng=(pws(n,1)*(LL)nn-h)%MOD,nh=(pws(nn,1)*(LL)n-g)%MOD;
g=ng;h=nh;
}
}
void answer(int a,int b,int c,int n,LL &f,LL &g,LL &h){
++n;
b-=a;
calc(a,b,c,n,f,g,h);
f=(f+MOD*MOD)%MOD;g=(g+MOD*MOD)%MOD;h=(h+MOD*MOD)%MOD;
h=(h*2ll+MOD-f)%MOD;
g=(g+MOD-f)%MOD;
}

(长期更新)OI常用模板的更多相关文章

  1. OI 常用模板 手写

    线性筛素数 (例题 洛谷P3383) bool p[50000010]; int cnt = 0; int prime[10000010]; inline void init() { int N = ...

  2. 【模板】OI常用模板(待补充)

    //PS:最近修改日期:2017-11-07 20:41:44 首先感觉这种模板类的东西写了还是很有意义的,毕竟时不时的可以拿出来借鉴一下. 现在因为刚开始写这一类的东西,所以说还不是很详细,若有读者 ...

  3. OI常用模板

    long long qpow(long long a,long long b,int mod) { ; while (b) { ) res=res*a%mod; a=a*a%mod; b>> ...

  4. Ubuntu16.04系统美化、常用软件安装等,长期更新

    Ubuntu16.04系统美化.常用软件安装等,长期更新 IT之家啊 18-09-0915:00 因为我个人偏向于玩VPS.服务器之类的东西,所以一般我都是用CentOS.不过对于桌面版的Linux, ...

  5. Spring MVC & Boot & Cloud 技术教程汇总(长期更新)

    昨天我们发布了Java成神之路上的知识汇总,今天继续. Java成神之路技术整理(长期更新) 以下是Java技术栈微信公众号发布的关于 Spring/ Spring MVC/ Spring Boot/ ...

  6. NDK(10)Android.mk各属性简介,Android.mk 常用模板

    参考 : http://blog.csdn.net/hudashi/article/details/7059006 本文内容: Android.mk简介, 各属性表, 常用Android.mk模板 1 ...

  7. [原创][FPGA]Quartus实用小技巧(长期更新)

    0. 简介 在使用Quartus软件时,经常会时不时的发现一些小技巧,本文的目的是总结所查阅或者发现到的小技巧,本文长期更新. 1. Quartus中的模板功能 最近在Quartus II的菜单里找到 ...

  8. NDK(10)Android.mk各属性简介,Android.mk 常用模板--未完

    参考 : http://blog.csdn.net/hudashi/article/details/7059006 1. Android.mk简介 Android.mk文件是GNU Makefile的 ...

  9. Java代码优化(长期更新)

    前言 2016年3月修改,结合自己的工作和平时学习的体验重新谈一下为什么要进行代码优化.在修改之前,我的说法是这样的: 就像鲸鱼吃虾米一样,也许吃一个两个虾米对于鲸鱼来说作用不大,但是吃的虾米多了,鲸 ...

随机推荐

  1. March 31 2017 Week 13 Friday

    Sometimes, you think the sky is falling down, actually, that is just because you stand slanting. 有时候 ...

  2. wireshark抓取本地回环数据包

      linux环境下,用tcpdump,可以用-i lo参数抓取环回接口的包.如果服务端和客户端安装在同一台机器上,调试时是很方便的.linux版的wireshark,选取网卡的菜单里也有lo选项,也 ...

  3. python自动化下载yunfile(未完成)

    参考https://www.cnblogs.com/qqandfqr/p/7866650.html import re import requests import pytesseract impor ...

  4. 【9.29 模拟】T3 小清新最优化(easy)

    [题目描述] 给出一个长度为 n 的序列,序列的每个元素为一个二元组,代表一种单目运算: • \((0,x)\): 对于一个数\(a\),将其变为 \(a\&x\).\((\&=x)\ ...

  5. 2019年5月训练记录(更新ing)

    前言 \(ZJOI\)正式结束了. 但期中考试只考了年级\(216\),退役既视感... 于是就被抓回去补文化课了. 下半个学期可能要以文化课为主了吧! 但周三.周日应该还是会正常参加训练的,但其他时 ...

  6. D - Going Home POJ - 2195 网络流

    On a grid map there are n little men and n houses. In each unit time, every little man can move one ...

  7. 【luogu P3808 AC自动机(简单版)】 模板

    题目链接:https://www.luogu.org/problemnew/show/P3808 #include <queue> #include <cstdio> #inc ...

  8. 【题解】洛谷P1006传纸条

    链接 https://www.luogu.org/problemnew/show/P1006 日常牢骚 过年前最后一节课上完了坐标DP 也接触了一点区间DP(noi1995石子合并)下次做做看看吧 老 ...

  9. Python—面向对象01

    1.如何使用类 # 先定义类 class LuffyStudent(): school = "luffycity" # 数据属性 def learn(self): # 函数属性 p ...

  10. Python基础—06-函数基础

    函数基础 函数简介 定义:就是具有特定功能的一段代码 优点: 解决代码的重复书写 可以将功能的实现着和使用者分开,提高开发效率 分类: 库函数:print.input.abs等 自定义:用户自己封装的 ...