SDOI2019 省选前模板整理

代码不能折叠,不过就这样吧。有目录也还好。
wdm好多。。
可能要下辈子才能全填上✔了。✘✘✘
Update:模板不复习,考场两行泪。
我要是复习MillerRabin和快速乘就多十八分了啊TAT
不过感谢复习了可持久化Trie。


计算几何✔

最近才写了,过了过了。虽然应该忘得差不多了。
旋转卡壳:经常要找和当前直线平行的直线切在哪里,注意用底相同,平行线之间高也相同,以及叉积有正负的性质,求叉积。


DP

斜率优化✔

将DP式子写成(y_j=kx_j+b)的形式,其中(k)是与(i)有关的系数,(b)是要求的(f_i)(i)的其他项。

四边形不等式✔

(f)满足四边形不等式,则决策单调,有(P[i][j-1]leq P[i][j]leq P[i+1][j])(P[i-1][j]leq P[i][j]leq P[i][j+1]),可根据需要使用。
具体看这里
实际上更大的用处是水过某些DP题...

轮廓线DP✘

nmd这东西根本写不出来的啊。


各种分治

CDQ分治✔

//6196KB	688MS(308ms	5560KB)
//https://www.cnblogs.com/SovietPower/p/8574905.html
#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
#define MAXIN 300000
//#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=1e5+5,M=2e5+5;

int Ans[N];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Node
{
	int x,y,z,cnt,ans;
	bool operator <(const Node &a)const
	{
		return x==a.x?(y==a.y?z<a.z:y<a.y):x<a.x;
	}
	bool operator !=(const Node &a)const
	{
		return x!=a.x||y!=a.y||z!=a.z;
	}
}q[N],tmp[N];
struct BIT
{
	int n,t[M];//M!
	#define lb(x) (x&-x)
	inline void Add(int p,int v)
	{
		for(; p<=n; p+=lb(p)) t[p]+=v;
	}
	inline int Query(int p)
	{
		int res=0;
		for(; p; p^=lb(p)) res+=t[p];
		return res;
	}
	inline void Clear(int p)
	{
		for(; p<=n&&t[p]; p+=lb(p)) t[p]=0;
	}
}T;

inline int read()
{
	int now=0;register char c=gc();
	for(;!isdigit(c);c=gc());
	for(;isdigit(c);now=now*10+c-48,c=gc());
	return now;
}
void CDQ(int l,int r)
{
	if(l<r)
	{
		int m=l+r>>1; CDQ(l,m), CDQ(m+1,r);
		int p1=l,p2=m+1,p=l;
		while(p1<=m&&p2<=r)
		{
			if(q[p1].y<=q[p2].y) T.Add(q[p1].z,q[p1].cnt), tmp[p++]=q[p1++];//q[p1].cnt!
			else q[p2].ans+=T.Query(q[p2].z), tmp[p++]=q[p2++];
		}
		while(p2<=r) q[p2].ans+=T.Query(q[p2].z), tmp[p++]=q[p2++];
		for(int i=l; i<p1; ++i) T.Clear(q[i].z);//<p1
		while(p1<=m) tmp[p++]=q[p1++];
		for(int i=l; i<=r; ++i) q[i]=tmp[i];
	}
}

int main()
{
	int n=read(); T.n=read();
	for(int i=1; i<=n; ++i) q[i]=(Node){read(),read(),read(),1,0};//cnt不能是0啊= = 
	std::sort(q+1,q+1+n); int cnt=1;
	for(int i=2; i<=n; ++i)
		if(q[i]!=q[i-1]) q[++cnt]=q[i];
		else ++q[cnt].cnt;
	CDQ(1,cnt);
	for(int i=1; i<=cnt; ++i) Ans[q[i].ans+q[i].cnt-1]+=q[i].cnt;
	for(int i=0; i<n; ++i) printf("%d
",Ans[i]);

	return 0;
}

点分治✔

过。

整体二分✔

//2740kb	132ms
//https://www.cnblogs.com/SovietPower/p/9231606.html
//原数列的值,要在树状数组上减掉啊。
#include <cstdio>
#include <cctype>
#include <algorithm>
//#define gc() getchar()
#define MAXIN 400000
#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=1e5+5,M=N*3;

int A[N],Ans[N];
char IN[MAXIN],*SS=IN,*TT=IN;
struct Operation
{
	int l,r,k,id;//id=0:A[l]=r 变化量为k 
}q[M];
struct BIT
{
	int n,t[N];
	#define lb(x) (x&-x)
	inline void Add(int p,int v)
	{
		for(; p<=n; p+=lb(p)) t[p]+=v;
	}
	inline int Query(int p)
	{
		int res=0;
		for(; p; p^=lb(p)) res+=t[p];
		return res;
	}
	inline void Clear(int p)
	{
		for(; p<=n&&t[p]; p+=lb(p)) t[p]=0;
	}
}T;

inline int read()
{
	int now=0;register char c=gc();
	for(;!isdigit(c);c=gc());
	for(;isdigit(c);now=now*10+c-48,c=gc());
	return now;
}
inline char GetOpt()
{
	register char c=gc(); while(c!='Q'&&c!='C') c=gc();
	return c;
}
void Solve(int l,int r,int h,int t)
{
	static Operation q1[M],q2[M];
	if(h>t) return;
	if(l==r)
	{
		for(int i=h; i<=t; ++i) Ans[q[i].id]=l;
		return;
	}
	bool fg=0;
	for(int i=h; i<=t; ++i) if(q[i].id) {fg=1; break;}
	if(!fg) return;
	int m=l+r>>1,t1=0,t2=0;
	for(int i=h; i<=t; ++i)
		if(q[i].id)
		{
			int tmp=T.Query(q[i].r)-T.Query(q[i].l-1);
			if(tmp>=q[i].k) q1[t1++]=q[i];
			else q[i].k-=tmp, q2[t2++]=q[i];
		}
		else if(q[i].r<=m) T.Add(q[i].l,q[i].k), q1[t1++]=q[i];
		else q2[t2++]=q[i];
	for(int i=h; i<=t; ++i) if(!q[i].id&&q[i].r<=m) T.Clear(q[i].l);
	for(int i=h,p=0; p<t1; q[i++]=q1[p++]);
	for(int i=h+t1,p=0; p<t2; q[i++]=q2[p++]);
	Solve(l,m,h,h+t1-1), Solve(m+1,r,h+t1,t);
}

int main()
{
	int n=read(),Q=read(),tot=0,now=n,mn=1e9,mx=0;
	for(int i=1; i<=n; ++i) q[i]=(Operation){i,A[i]=read(),1,0},mx=std::max(mx,A[i]),mn=std::min(mn,A[i]);
	for(int i=1,p; i<=Q; ++i)
		if(GetOpt()=='Q') q[++now]=(Operation){read(),read(),read(),++tot};
		else p=read(),q[++now]=(Operation){p,A[p],-1,0},q[++now]=(Operation){p,A[p]=read(),1,0},mx=std::max(mx,A[p]),mn=std::min(mn,A[p]);//修改原数列!
	T.n=n, Solve(mn,mx,1,now);
	for(int i=1; i<=tot; ++i) printf("%d
",Ans[i]);

	return 0;
}

数据结构

线段树、树状数组、Trie树、树剖、二维线段树、主席树略
树分块...我还不会卡不掉的树分块。。。

线段树合并✔

Merge的时候不要写Update(x)就好了...

分块✔

查询区间(k)的倍数时,对于整块加(val),可以变成求这块模(k=k-val)的数的个数,这样只需要维护一个模数标记(初始为(0)),这样就将整块加转化成了标记的(O(1))修改。
这个思路挺好的,应该能拓展。

K-D Tree

LCT

http://www.cnblogs.com/SovietPower/category/1182810.html

//520ms	2984KB
//https://www.cnblogs.com/SovietPower/p/8615938.html
#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
#define MAXIN 300000
//#define gc() (SS==TT&&(TT=(SS=IN)+fread(IN,1,MAXIN,stdin),SS==TT)?EOF:*SS++)
typedef long long LL;
const int N=3e5+5;

char IN[MAXIN],*SS=IN,*TT=IN;
struct LCT
{
	#define ls son[x][0]
	#define rs son[x][1]
	int fa[N],son[N][2],sum[N],val[N],sk[N];
	bool rev[N];
	inline void Update(int x)
	{
		sum[x]=sum[ls]^sum[rs]^val[x];
	}
	inline bool n_root(int x)
	{
		return son[fa[x]][0]==x||son[fa[x]][1]==x;
	}
	inline void Rev(int x)
	{
		std::swap(ls,rs), rev[x]^=1;
	}
	inline void PushDown(int x)
	{
		if(rev[x]) Rev(ls), Rev(rs), rev[x]=0;
	}
	void Rotate(int x)
	{
		int a=fa[x],b=fa[a],l=son[a][1]==x,r=l^1;
		if(n_root(a)) son[b][son[b][1]==a]=x;
		if(son[x][r]) fa[son[x][r]]=a;
		fa[x]=b, fa[a]=x, son[a][l]=son[x][r], son[x][r]=a;
		Update(a);
	}
	void Splay(int x)
	{
		int t=1,a=x; sk[1]=a;
		while(n_root(a)) sk[++t]=a=fa[a];
		while(t) PushDown(sk[t--]);
		while(n_root(x))
		{
			if(n_root(a=fa[x])) Rotate(son[a][0]==x^son[fa[a]][0]==a?x:a);
			Rotate(x);
		}
		Update(x);
	}
	void Access(int x)
	{
		for(int pre=0; x; x=fa[pre=x])
			Splay(x), rs=pre, Update(x);
	}
	void MakeRoot(int x)
	{
		Access(x), Splay(x), Rev(x);
	}
	void Split(int x,int y)
	{
		MakeRoot(x), Access(y), Splay(y);
	}
	int FindRoot(int x)
	{
		Access(x), Splay(x);
		while(ls) x=ls;
		return x;
	}
	void Link(int x,int y)
	{
		MakeRoot(x);
		if(FindRoot(y)!=x) fa[x]=y;
	}
	void Cut(int x,int y)
	{
		MakeRoot(x);
		if(FindRoot(y)==x&&fa[x]==y&&!rs) fa[x]=son[y][0]=0, Update(y);
	}
}T;

inline int read()
{
	int now=0;register char c=gc();
	for(;!isdigit(c);c=gc());
	for(;isdigit(c);now=now*10+c-48,c=gc());
	return now;
}

int main()
{
	int n=read(),q=read();
	for(int i=1; i<=n; ++i) T.val[i]=read();
	for(int x,y; q--; )
		switch(read())
		{
			case 0: x=read(),y=read(),T.Split(x,y),printf("%d
",T.sum[y]); break;
			case 1: x=read(),y=read(),T.Link(x,y); break;
			case 2: x=read(),y=read(),T.Cut(x,y); break;
			case 3: T.Splay(x=read()),T.val[x]=read(); break;
		}

	return 0;
}

可持久化Trie✔

https://i.cnblogs.com/posts?categoryid=1161664

Splay

http://www.cnblogs.com/SovietPower/p/8435011.html

fhq Treap

http://www.cnblogs.com/SovietPower/p/8431909.html

虚树

可并堆 左偏树*

http://www.cnblogs.com/SovietPower/p/8435041.html


数学,数论

CRT

扩展CRT

Lucas

扩展Lucas

杜教筛✔

Min25筛

莫比乌斯反演✔

FFT,NTT✔

#include <cmath>
#include <cstdio>
#include <cctype>
#include <algorithm>
#define gc() getchar()
typedef long long LL;
const int N=(1<<21)+5;
const double PI=acos(-1);

int rev[N];
struct Complex
{
	double x,y;
	Complex(double x=0,double y=0):x(x),y(y) {}
	inline Complex operator +(const Complex &a)const {return Complex(x+a.x, y+a.y);}
	inline Complex operator -(const Complex &a)const {return Complex(x-a.x, y-a.y);}
	inline Complex operator *(const Complex &a)const {return Complex(x*a.x-y*a.y, x*a.y+y*a.x);}
}f[N],g[N],W[N];

inline int read()
{
	int now=0;register char c=gc();
	for(;!isdigit(c);c=gc());
	for(;isdigit(c);now=now*10+c-48,c=gc());
	return now;
}
void FFT(Complex *a,int lim,int opt)
{
	static Complex w[N];
	for(int i=0; i<lim; ++i) if(i<rev[i]) std::swap(a[i],a[rev[i]]);
	for(int i=2; i<=lim; i<<=1)
	{
		int mid=i>>1;
		Complex Wn(cos(PI/mid),opt*sin(PI/mid));
		for(int j=0; j<lim; j+=i)
		{
			Complex w(1,0),t;
			for(int k=j; k<j+mid; ++k,w=w*Wn)
				a[k+mid]=a[k]-(t=w*a[k+mid]), a[k]=a[k]+t;
		}
//		if(~opt) for(int k=0,t=lim/i; k<mid; ++k) w[k]=W[k*t];
//		else for(int k=0,t=lim/i; k<mid; ++k) w[k]=Complex(W[k*t].x,-W[k*t].y);
//		for(int j=0; j<lim; j+=i)
//		{
//			Complex t;
//			for(int k=j; k<j+mid; ++k)
//				a[k+mid]=a[k]-(t=w[k-j]*a[k+mid]), a[k]=a[k]+t;
//		}
	}
	if(opt==-1) for(int i=0; i<lim; ++i) a[i].x/=lim;
}

int main()
{
	int n=read()+1,m=read()+1;
	for(int i=0; i<n; ++i) f[i].x=read();
	for(int i=0; i<m; ++i) g[i].x=read();
	int lim=1,bit=-1;
	while(lim<=n+m) lim<<=1,++bit;
	for(int i=1; i<lim; ++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<bit);
//	for(int i=0,t=lim>>1; i<lim; ++i) W[i]=Complex(cos(i*PI/t),sin(i*PI/t));

	FFT(f,lim,1), FFT(g,lim,1);
	for(int i=0; i<lim; ++i) f[i]=f[i]*g[i];
	FFT(f,lim,-1);
	for(int i=0,l=n+m-2; i<=l; ++i) printf("%d ",int(f[i].x+0.5));

	return 0;
}
#include <cstdio>
#include <cctype>
#include <algorithm>
#define mod 998244353
#define G 3
#define invG 332748118
#define Mod(x) x>=mod&&(x-=mod)
#define gc() getchar()
typedef long long LL;
const int N=(1<<21)+5;

int f[N],g[N],rev[N];

inline int read()
{
	int now=0;register char c=gc();
	for(;!isdigit(c);c=gc());
	for(;isdigit(c);now=now*10+c-48,c=gc());
	return now;
}
inline int FP(int x,int k)
{
	int t=1;
	for(; k; k>>=1,x=1ll*x*x%mod) k&1&&(t=1ll*t*x%mod);
	return t;
}
void NTT(int *a,int lim,int opt)
{
	for(int i=0; i<lim; ++i) if(i<rev[i]) std::swap(a[i],a[rev[i]]);
	for(int i=2; i<=lim; i<<=1)
	{
		int mid=i>>1; LL Wn=FP(~opt?G:invG,(mod-1)/i);
		for(int j=0; j<lim; j+=i)
			for(int k=j,t,w=1; k<j+mid; ++k,w=w*Wn%mod)
				a[k+mid]=a[k]+mod-(t=1ll*w*a[k+mid]%mod), Mod(a[k+mid]), a[k]+=t, Mod(a[k]);
	}
	if(opt==-1) for(int i=0,inv=FP(lim,mod-2); i<lim; ++i) a[i]=1ll*a[i]*inv%mod;
}

int main()
{
	int n=read()+1,m=read()+1;
	for(int i=0; i<n; ++i) f[i]=read();
	for(int i=0; i<m; ++i) g[i]=read();
	int lim=1,bit=-1;
	while(lim<=n+m) lim<<=1,++bit;
	for(int i=1; i<lim; ++i) rev[i]=(rev[i>>1]>>1)|((i&1)<<bit);

	NTT(f,lim,1), NTT(g,lim,1);
	for(int i=0; i<lim; ++i) f[i]=1ll*f[i]*g[i]%mod;
	NTT(f,lim,-1);
	for(int i=0,l=n+m-2; i<=l; ++i) printf("%d ",f[i]);

	return 0;
}

FWT✔

BSGS

Miller Rabin*

Pollard Rho*

Catalan数 Stirling数

高斯消元

拉格朗日插值✔

单纯形*

线性基


博弈论


图论

最短路、生成树、次小生成树略。

Prufer序列

Tarjan

差分约束

二分图匹配

欧拉路、哈密顿路

圆方树

支配树*

2-SAT*

Matrix Tree*

http://www.cnblogs.com/SovietPower/p/8463968.html


网络流

Dinic、ISAP、费用流略。

无源汇上下界网络流✔

有源汇上下界网络流✔

有源汇上下界最小流✔


字符串

KMP✔

https://www.luogu.org/recordnew/show/15171303

Manacher✔

http://www.cnblogs.com/SovietPower/p/8677979.html

回文树✔

//110ms	34876KB
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define gc() getchar()
typedef long long LL;
const int N=3e5+5;

struct PAM
{
	int tot,las,fail[N],len[N],val[N],son[N][26];
	char s[N];
	PAM() {tot=1, las=0, fail[0]=1, len[1]=-1, s[0]='$';}
	inline int Find(int x,int n)
	{
		while(s[n-len[x]-1]!=s[n]) x=fail[x];
		return x;
	}
	void Insert(int c,int n)
	{
		int p=Find(las,n);
		if(!son[p][c])
		{
			int np=++tot; fail[np]=son[Find(fail[p],n)][c];
			son[p][c]=np, len[np]=len[p]+2;
		}
		++val[las=son[p][c]];
	}
	void Solve()
	{
		scanf("%s",s+1); int n=strlen(s+1);
		for(int i=1; i<=n; ++i) Insert(s[i]-'a',i);
		LL ans=0;
		for(int x=tot; x>1; --x)
			ans=std::max(ans,1ll*val[x]*len[x]), val[fail[x]]+=val[x];
		printf("%lld
",ans);
	}
}pam;

int main()
{
	pam.Solve();
	return 0;
}

后缀数组✔

//297ms	15228kb
#include <cstdio>
#include <cctype>
#include <cstring>
#include <algorithm>
#define gc() getchar()
typedef long long LL;
const int N=1e6+5;

struct Suffix_Array
{
	int rk[N],sa[N],sa2[N],tm[N],ht[N];
	char s[N];
	void Build(char *s,int n,int m)
	{
		int *x=rk,*y=sa2;
		for(int i=0; i<=m; ++i) tm[i]=0;
		for(int i=1; i<=n; ++i) ++tm[x[i]=s[i]-'a'+1];
		for(int i=1; i<=m; ++i) tm[i]+=tm[i-1];
		for(int i=n; i; --i) sa[tm[x[i]]--]=i;
		for(int k=1,p=0; k<n; k<<=1,m=p,p=0)
		{
			for(int i=n-k+1; i<=n; ++i) y[++p]=i;
			for(int i=1; i<=n; ++i) if(sa[i]>k) y[++p]=sa[i]-k;

			for(int i=1; i<=m; ++i) tm[i]=0;
			for(int i=1; i<=n; ++i) ++tm[x[i]];
			for(int i=1; i<=m; ++i) tm[i]+=tm[i-1];
			for(int i=n; i; --i) sa[tm[x[y[i]]]--]=y[i];

			std::swap(x,y), x[sa[1]]=p=1;
			for(int i=2; i<=n; ++i)
				x[sa[i]]=(y[sa[i]]==y[sa[i-1]]&&y[sa[i]+k]==y[sa[i-1]+k])?p:++p;
			if(p>=n) break;
		}
		for(int i=1; i<=n; ++i) rk[sa[i]]=i;
		ht[1]=0;
		for(int i=1,k=0; i<=n; ++i)
		{
			if(rk[i]==1) continue;
			if(k) --k;
			int p=sa[rk[i]-1];
			while(i+k<=n && p+k<=n && s[i+k]==s[p+k]) ++k;
			ht[rk[i]]=k;
		}
	}
	void Solve()
	{
		scanf("%s",s+1); int n=strlen(s+1);
		Build(s,n,27);
		for(int i=1; i<=n; ++i) printf("%d ",sa[i]); puts("");
		for(int i=2; i<=n; ++i) printf("%d ",ht[i]); puts("");
	}
}sa;

int main()
{
	sa.Solve();
	return 0;
}

AC自动机

http://www.cnblogs.com/SovietPower/p/8530327.html

后缀自动机


其它

dsu on tree

长链剖分

高维前缀和✔

带权二分

模拟退火

一般莫队✔

常搭配分块使用(莫队本身(O(nsqrt n))次修改,(O(n))次查询)。
优化什么的(分奇偶),不管惹。

带修改莫队

回滚莫队✔

还是将所有询问按((左端点所在块, 右端点))排序。对于左右端点在同一块里的询问,暴力查询。
否则,把左端点在同一块(i)里的询问一起处理。这时右端点一定是递增的。
(l)为第(i+1)块(下一块)的左端点,(r)为第(i)块(该块)的右端点。将(r)移动到当前询问的右端点处并更新答案,记录此时答案(tmp=Now)
(l)移动到当前询问的左端点处并更新答案,得到当前询问的答案(Now)
(l)移回到下一块左端点位置,并消除影响(减掉加的次数之类的),并令(Now=tmp)

原文地址:https://www.cnblogs.com/SovietPower/p/10655291.html