(长期更新)OI常用模板

复杂模板

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

常系数齐次线性递推
Code1(2.5s, 2.8k):提交记录
Code2(2.3s, 3.0k):提交记录

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

不同于某些无脑写法,这里的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

线段树合并与分裂

#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(){
			pfa=fa->pfa;
			int d=pos();
			node *F=fa,*uc=s[!d];
			fa=F->fa;
			if(fa) fa->s[F->pos()]=this;
			F->s[d]=uc;
			if(uc) uc->fa=F;
			s[!d]=F;
			F->fa=this;
			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();
	}
};

动态直线下凸壳
支持\(\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;
	}
};

替罪羊式的K-D Tree
题目链接
在平面上支持插入点、查询曼哈顿最近点。
以下代码中需要注意:两处注释了Mark的位置中,dep&1不能替换成dim,因为在处理右儿子时,dim可能已经被左儿子修改。

UPD:这份代码虽然正确性有保障,但有以下几处缺陷:

  • 一个结点所控制的矩形,并未设为其管辖的全体关键点的Bounding Box,而是一个可能比Bounding Box更大的矩形。这可能导致询问时常数增大。
  • 没有使用内存池。这可能导致建树和修改/询问时的常数增大,原因一方面是new的速度较慢;另一方面是new出来的元素在空间上不连续。
  • build函数传参时传的是数组指针而不是数组下标。这可能使某些操作不太方便。

上述问题都容易自行解决,所以对以下代码未作更新。一份更优秀的板子可以看我的提交记录,UOJ这道题要支持的操作是平面上在圆形范围内数点,数据随机。

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; // Mark
			else	  s[0]->xr=cen.FS;
		}
		else{
			if(dep&1) s[1]->yl=cen.SC; // Mark
			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算法求解最短线性递推式
以下代码可能有锅,有待检查。(2020/7/23)

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;
}

eertree + border group

int vs,nx[100005][26],fail[100005],len[100005],dif[100005],selink[100005],alls[100005];
int n;
char s[100005];
void init(){
	vs=0;
	memset(nx,-1,sizeof(nx));
	memset(dif,-1,sizeof(dif));
	memset(fail,-1,sizeof(fail));
	memset(selink,-1,sizeof(selink));
}
vector < int > getbg(int l,int r){// 求s[l...r]的border集合,返回每个等差数列的公差
	vector < int > ret;
	int i,tl=r-l+1;
	for(i=alls[r];~i && len[i]>0;i=selink[i]){
		if(!~selink[i] || len[selink[i]]<tl)
			ret.push_back(dif[i]);
	}
	return ret;
}
void solve(){
	int i,j,k;
	init();
	scanf("%s",s);n=strlen(s);
	vs=2;// -1,0
	fail[1]=fail[0]=0;
	len[0]=-1;len[1]=0;
	selink[1]=0;
	selink[0]=0;
	int last=0;
	for(i=0;i<n;++i){
		int c=s[i]-'a';
		if(s[i]>='A' && s[i]<='Z') c=s[i]-'A';
		while(len[last]>=i || s[i-1-len[last]]!=s[i])
			last=fail[last];
		if(~nx[last][c])
			last=nx[last][c];
		else{
			int &q=nx[last][c];
			q=vs++;
			len[q]=len[last]+2;
			for(last=fail[last];!~nx[last][c] || s[i-1-len[last]]!=s[i];last=fail[last]);
			fail[q]=nx[last][c]==q?1:nx[last][c];
			dif[q]=len[q]-len[fail[q]];
			selink[q]=dif[q]==dif[fail[q]]?selink[fail[q]]:fail[q];
			last=q;
		}
		alls[i]=last;
	}
}

简单模板

Dinic最大流

const int S=101,T=102;
namespace flows{
    struct edge{
        int v,r,f;
        edge(int V,int R,int F){
            v=V;r=R;f=F;
        }
    };
    vector < edge > adj[105];
    int cur[105],dis[105];
    void init(){
        int i;
        for(i=0;i<105;++i){
            adj[i].clear();
        }
    }
    void addedge(int u,int v,int f){
        adj[u].push_back(edge(v,(int)adj[v].size(),f));
        adj[v].push_back(edge(u,(int)adj[u].size()-1,0));
    }
    void bfs(){
        queue < int > Q;
        Q.push(S);
        dis[S]=0;
        while(!Q.empty()){
            int i,cv=Q.front();
            Q.pop();
            for(i=0;i<(int)adj[cv].size();++i){
                if(adj[cv][i].f>0 && dis[adj[cv][i].v]==-1){
                    dis[adj[cv][i].v]=dis[cv]+1;
                    Q.push(adj[cv][i].v);
                } 
            }
        }
    }
    int dfs(int ver,int cap){
        if(ver==T) return cap;
        for(int &i=cur[ver];i<(int)adj[ver].size();++i){
            edge &E=adj[ver][i];
            if(E.f>0 && dis[E.v]>dis[ver]){
                int ret=dfs(E.v,min(cap,E.f));
                if(ret>0){
                    E.f-=ret;
                    adj[E.v][E.r].f+=ret;
                    return ret;
                }
            }
        }
        return 0;
    }
    int maxflow(){
        int ret=0,dlt;
        while(1){
            memset(dis,-1,sizeof(dis));
            memset(cur,0,sizeof(cur));
            bfs();
            if(dis[T]==-1) return ret;
            dlt=1;
            while(dlt){
                dlt=dfs(S,(int)1e9);
                ret+=dlt;
            }
        }
    }
}

线性基

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;
				}
			}
		}
	}
};

可持久化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();
	}
}

自适应辛普森积分

double simpson(double L,double R){
	return (eval((L+R)/2)*4.0+eval(L)+eval(R))*(R-L)/6;
}
double solve(double rngL,double rngR,double maxeps,double esti){
	double rngM=(rngL+rngR)/2;
	double estiL=simpson(rngL,rngM),estiR=simpson(rngM,rngR);
	if(abs(estiL+estiR-esti)<15*maxeps)
		return estiL+estiR+(estiL+estiR-esti)/15;
	return solve(rngL,rngM,maxeps/2,estiL)+solve(rngM,rngR,maxeps/2,estiR);
}
posted @ 2018-06-04 23:08  TianyiQ  阅读(1181)  评论(0)    收藏  举报