模板综合

把一些感觉不太熟的模板再敲一敲吧。

文章目录

      • 图论
          • 割点 & \& & 割边
          • 点双连通分量
          • 边双联通分量
      • 数据结构
          • 树链剖分换根
          • ST
          • 左偏树
          • FHQ_Treap
          • LCT
      • 字符串
          • AC 自动机
          • SAM
          • PAM
      • 数论
          • exCRT
          • BSGS
          • 高斯消元
          • 二次剩余


图论

割点 & \& & 割边

source:「HihoCoder 1183」割边与割点

#include
using namespace std;
typedef pair<int,int> pii;
namespace IO{
	const int Rlen=1<<22|1;
	char buf[Rlen],*p1,*p2;
	inline char gc(){
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	template<typename T>
	inline T Read(){
		char c=gc();T x=0,f=1;
		while(!isdigit(c))  f^=(c=='-'),c=gc();
		while( isdigit(c))  x=((x+(x<<2))<<1)+(c^48),c=gc();
		return f?x:-x;
	}
	inline int in()  {return Read<int>();}
}
using IO::in;
const int N=2e4+5,M=1e5+5;
int n,m,tot,root;
int dfn[N],low[N],cut[N],bridge[M<<1];
int t=1,first[N],v[M<<1],nxt[M<<1];
void add(int x,int y)  {nxt[++t]=first[x],first[x]=t,v[t]=y;}
void Max(int &x,int y)  {if(x<y)x=y;}
void Min(int &x,int y)  {if(x>y)x=y;}
void Clear()  {tot=0,memset(dfn,0,sizeof(dfn));}
void Tarjan_cut(int x,int son=0){
	dfn[x]=low[x]=++tot;
	for(int i=first[x];i;i=nxt[i]){
		int to=v[i];
		if(!dfn[to]){
			son++,Tarjan_cut(to),Min(low[x],low[to]);
			cut[x]|=(root==x&&son>1)|(root!=x&&low[to]>=dfn[x]);
		}
		else  Min(low[x],dfn[to]);
	}
}
void Tarjan_bridge(int x,int pre){
	dfn[x]=low[x]=++tot;
	for(int i=first[x];i;i=nxt[i]){
		int to=v[i];
		if(i==(pre^1))  continue;
		if(!dfn[to]){
			Tarjan_bridge(to,i),Min(low[x],low[to]);
			if(low[to]>dfn[x])  bridge[i]=bridge[i^1]=1;
		}
		else  Min(low[x],dfn[to]);
	}
}
vector<int>point;
vector<pii>edges;
int main(){
	n=in(),m=in();
	for(int i=1,x,y;i<=m;++i)  x=in(),y=in(),add(x,y),add(y,x);
	Clear(),Tarjan_cut(root=1);
	Clear(),Tarjan_bridge(1,0);
	for(int i=1;i<=n;++i)  if(cut[i])  point.push_back(i);
	for(int i=2;i<t;i+=2)  if(bridge[i])  edges.push_back((v[i]<v[i^1])?pii(v[i],v[i^1]):pii(v[i^1],v[i]));
	sort(edges.begin(),edges.end());
	if(point.empty())  puts("Null");
	for(int i=0;i<point.size();++i)  printf("%d%c",point[i],(i==point.size()-1)?'\n':' ');
	for(int i=0;i<edges.size();++i)  printf("%d %d\n",edges[i].first,edges[i].second);
	return 0;
}

点双连通分量

source:「HNOI 2012」矿场修建

解析就去博客里看吧,这里存一下点双的代码。

#include
using namespace std;
typedef long long ll;
const int N=1005;
int n,m,root,tot,num;
int dfn[N],low[N],cut[N],vis[N];
int t,first[N],v[N],nxt[N];
void add(int x,int y)  {nxt[++t]=first[x],first[x]=t,v[t]=y;}
void Max(int &x,int y)  {if(x<y)x=y;}
void Min(int &x,int y)  {if(x>y)x=y;}
void Clear(){
	n=t=tot=num=0;
	memset(first,0,sizeof(first));
	memset(dfn,0,sizeof(dfn));
	memset(low,0,sizeof(low));
	memset(vis,0,sizeof(vis));
	memset(cut,0,sizeof(cut));
}
void Tarjan(int x,int son=0){
	dfn[x]=low[x]=++tot;
	for(int i=first[x];i;i=nxt[i]){
		int to=v[i];
		if(!dfn[to]){
			son++,Tarjan(to),Min(low[x],low[to]);
			cut[x]|=(root==x&&son>1)|(root!=x&&low[to]>=dfn[x]);
		}
		else  Min(low[x],dfn[to]);
	}
}
int cnt_cut,cnt_pnt,ans1;
ll ans2;
void dfs(int x,int id){
	vis[x]=id,cnt_pnt++;
	for(int i=first[x];i;i=nxt[i]){
		int to=v[i];
		if(cut[to]&&vis[to]!=id)  vis[to]=id,cnt_cut++;
		if(!vis[to])  dfs(to,id);
	}
}
int main(){
	int Case=0;
	while((~scanf("%d",&m))&&m){
		Clear();
		for(int i=1,x,y;i<=m;++i){
			scanf("%d%d",&x,&y);
			add(x,y),add(y,x),Max(n,max(x,y));
		}
		for(int i=1;i<=n;++i)
			if(!dfn[i])  Tarjan(root=i);
		ans1=0,ans2=1;
		for(int i=1;i<=n;++i){
			if(cut[i]||vis[i])  continue;
			cnt_cut=cnt_pnt=0,dfs(i,++num);
			if(cnt_cut==0)  ans1+=2,ans2*=cnt_pnt*(cnt_pnt-1)/2;
			if(cnt_cut==1)  ans1+=1,ans2*=cnt_pnt;
		}
		printf("Case %d: %d %lld\n",++Case,ans1,ans2);
	}
	return 0;
}


边双联通分量

source:「HihoCoder 1184」边的双连通分量

代码和强联通分量出奇的像呢。

#include
using namespace std;
typedef pair<int,int> pii;
namespace IO{
	const int Rlen=1<<22|1;
	char buf[Rlen],*p1,*p2;
	inline char gc(){
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	template<typename T>
	inline T Read(){
		char c=gc();T x=0,f=1;
		while(!isdigit(c))  f^=(c=='-'),c=gc();
		while( isdigit(c))  x=((x+(x<<2))<<1)+(c^48),c=gc();
		return f?x:-x;
	}
	inline int in()  {return Read<int>();}
}
using IO::in;
const int N=2e4+5,M=1e5+5;
int n,m,tot,top,num;
int dfn[N],low[N],stk[N],id[N],bel[N];
int t=1,first[N],v[M<<1],nxt[M<<1];
void add(int x,int y)  {nxt[++t]=first[x],first[x]=t,v[t]=y;}
void Max(int &x,int y)  {if(x<y)x=y;}
void Min(int &x,int y)  {if(x>y)x=y;}
void Tarjan(int x,int pre){
	dfn[x]=low[x]=++tot,stk[++top]=x;
	for(int i=first[x];i;i=nxt[i]){
		int to=v[i];
		if(i==(pre^1))  continue;
		if(!dfn[to])  Tarjan(to,i),Min(low[x],low[to]);
		else  Min(low[x],dfn[to]);
	}
	int k;
	if(dfn[x]==low[x]){
		id[++num]=n+1;
		do{
			k=stk[top--],Min(id[num],k),bel[k]=num;
		}while(k!=x);
	}
}
int main(){
	n=in(),m=in();
	for(int i=1,x,y;i<=m;++i)  x=in(),y=in(),add(x,y),add(y,x);
	Tarjan(1,0),printf("%d\n",num);
	for(int i=1;i<=n;++i)  printf("%d ",id[bel[i]]);
	return 0;
}

数据结构

树链剖分换根

source:「校内网站 3784」树链剖分换根

复习一下换根的模板,分析可以看这里。

  • E x:将 x x x 设成根。
  • V x y:将点 x x x 的权值赋成 y y y
  • Q x:查询 x x x 子树权值最小值。

PS:换根是对链修改,链查询没有影响的,虽然这里用不着。

#include
using namespace std;
namespace IO{
	const int Rlen=1<<22|1;
	char buf[Rlen],*p1,*p2;
	inline char gc(){
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	template<typename T>
	inline T Read(){
		char c=gc();T x=0,f=1;
		while(!isdigit(c))  f^=(c=='-'),c=gc();
		while( isdigit(c))  x=((x+(x<<2))<<1)+(c^48),c=gc();
		return f?x:-x;
	}
	inline int in()  {return Read<int>();}
}
using IO::in;
using IO::gc;
const int N=1e5+5;
int n,Q,root=1;
int t,val[N],first[N],v[N],nxt[N];
void add(int x,int y)  {nxt[++t]=first[x],first[x]=t,v[t]=y;}
int tot,fa[N],dep[N],son[N],sze[N],top[N],pos[N],idx[N];
namespace SGT{
	int mn[N<<2];
	#define mid ((l+r)>>1)
	void build(int root,int l,int r){
		if(l==r)  {mn[root]=val[idx[l]];return;}
		build(root<<1,l,mid),build(root<<1|1,mid+1,r);
		mn[root]=min(mn[root<<1],mn[root<<1|1]);
	}
	void Modify(int root,int l,int r,int pos,int val){
		if(l==r)  {mn[root]=val;return;}
		if(pos<=mid)  Modify(root<<1,l,mid,pos,val);
		else  Modify(root<<1|1,mid+1,r,pos,val);
		mn[root]=min(mn[root<<1],mn[root<<1|1]);
	}
	int Query(int root,int l,int r,int x,int y){
		if(x>y)  return 2e9;
		if(l>=x&&r<=y)  return mn[root];
		if(y<=mid)  return Query(root<<1,l,mid,x,y);
		if(x> mid)  return Query(root<<1|1,mid+1,r,x,y);
		return min(Query(root<<1,l,mid,x,y),Query(root<<1|1,mid+1,r,x,y));
	}
	#undef mid
}
namespace Tree_cutting{
	void dfs1(int x){
		sze[x]=1;
		for(int i=first[x];i;i=nxt[i]){
			int to=v[i];
			fa[to]=x,dep[to]=dep[x]+1;
			dfs1(to),sze[x]+=sze[to];
			if(sze[to]>sze[son[x]])  son[x]=to;
		}
	}
	void dfs2(int x,int tp){
		top[x]=tp,idx[pos[x]=++tot]=x;
		if(son[x])  dfs2(son[x],tp);
		for(int i=first[x];i;i=nxt[i])
			if(v[i]!=son[x])  dfs2(v[i],v[i]);
	}
	int LCA(int x,int y){
		while(top[x]!=top[y]){
			if(dep[top[x]]<dep[top[y]])  swap(x,y);
			x=fa[top[x]];
		}
		return dep[x]<dep[y]?x:y;
	}
	int find(int x,int y){
		while(top[x]!=top[y]){
			if(dep[top[x]]<dep[top[y]])  swap(x,y);
			if(fa[top[x]]==y)  return top[x];
			x=fa[top[x]];
		}
		if(dep[x]>dep[y])  swap(x,y);
		return son[x];
	}
}
using namespace Tree_cutting;
int main(){
	n=in(),Q=in();
	for(int i=1,fa;i<=n;++i){
		fa=in(),val[i]=in();
		if(fa)  add(fa,i);
	}
	dfs1(1),dfs2(1,1),SGT::build(1,1,n);
	int x,y;
	while(Q--){
		char op=gc();
		while(op!='E'&&op!='V'&&op!='Q')  op=gc();
		if(op=='E')  root=in();
		else  if(op=='V')  x=in(),y=in(),SGT::Modify(1,1,n,pos[x],y);
		else{
			x=in();
			if(x==root)  {printf("%d\n",SGT::mn[1]);continue;}
			if(x!=LCA(x,root))  {printf("%d\n",SGT::Query(1,1,n,pos[x],pos[x]+sze[x]-1));continue;}
			int child=find(x,root);
			printf("%d\n",min(SGT::Query(1,1,n,1,pos[child]-1),SGT::Query(1,1,n,pos[child]+sze[child],n)));
		}
	}
	return 0;
}

ST

source:「洛谷 3865」ST

#include
using namespace std;
namespace IO{
	const int Rlen=1<<22|1;
	char buf[Rlen],*p1,*p2;
	inline char gc(){
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	template<typename T>
	inline T Read(){
		char c=gc();T x=0,f=1;
		while(!isdigit(c))  f^=(c=='-'),c=gc();
		while( isdigit(c))  x=((x+(x<<2))<<1)+(c^48),c=gc();
		return f?x:-x;
	}
	inline int in()  {return Read<int>();}
}
using IO::in;
const int N=1e5+5;
int n,m,Log[N],mx[N][18];
void prework(){
	for(int i=2;i<=n;++i)  Log[i]=Log[i>>1]+1;
	for(int j=1;(1<<j)<=n;++j)
		for(int i=1;i+(1<<(j-1))<=n;++i)
			mx[i][j]=max(mx[i][j-1],mx[i+(1<<(j-1))][j-1]);
}
int GetMax(int l,int r){
	int k=Log[r-l+1];
	return max(mx[l][k],mx[r-(1<<k)+1][k]);
}
int main(){
	n=in(),m=in();
	for(int i=1;i<=n;++i)  mx[i][0]=in();
	prework();
	while(m--){
		int l=in(),r=in();
		printf("%d\n",GetMax(l,r));
	}
	return 0;
}

左偏树

source:「洛谷 3377」左偏树

#include
using namespace std;
namespace IO{
	const int Rlen=1<<22|1;
	char buf[Rlen],*p1,*p2;
	inline char gc(){
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	template<typename T>
	inline T Read(){
		char c=gc();T x=0,f=1;
		while(!isdigit(c))  f^=(c=='-'),c=gc();
		while( isdigit(c))  x=((x+(x<<2))<<1)+(c^48),c=gc();
		return f?x:-x;
	}
	inline int in()  {return Read<int>();}
}
using IO::in;
const int N=1e5+5;
int n,m;
int fa[N],lc[N],rc[N],dis[N],val[N];
int find(int x)  {return (fa[x]==x)?x:fa[x]=find(fa[x]);}
int Merge(int x,int y){
	if(!x||!y)  return x+y;
	if(val[x]>val[y])  swap(x,y);
	rc[x]=Merge(rc[x],y);
	if(dis[lc[x]]<dis[rc[x]])  swap(lc[x],rc[x]);
	fa[lc[x]]=fa[rc[x]]=x,dis[x]=dis[rc[x]]+1;
	return x;
}
void Pop(int x){
	val[x]=-1;
	fa[lc[x]]=lc[x],fa[rc[x]]=rc[x];
	fa[x]=Merge(lc[x],rc[x]);
}
int main(){
	n=in(),m=in();
	for(int i=1;i<=n;++i)  fa[i]=i,val[i]=in();
	while(m--){
		int op=in();
		if(op==1){
			int x=in(),y=in();
			if(val[x]==-1||val[y]==-1||find(x)==find(y))  continue;
			x=find(x),y=find(y),Merge(x,y);
		}
		else{
			int x=in();
			if(val[x]==-1)  {printf("-1\n");continue;}
			x=find(x),printf("%d\n",val[x]),Pop(x);
		}
	}
	return 0;
}

FHQ_Treap

source:「洛谷 3369」普通平衡树

#include
using namespace std;
namespace IO{
	const int Rlen=1<<22|1;
	char buf[Rlen],*p1,*p2;
	inline char gc(){
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	template<typename T>
	inline T Read(){
		char c=gc();T x=0,f=1;
		while(!isdigit(c))  f^=(c=='-'),c=gc();
		while( isdigit(c))  x=(x+(x<<2)<<1)+(c^48),c=gc();
		return f?x:-x;
	}
	inline int in()  {return Read<int>();}
}
using IO::in;
const int N=1e5+5;
int n;
namespace FHQ{
	int root,tot,sze[N],lc[N],rc[N],val[N],key[N];
	int newnode(int v){
		int u=++tot;
		sze[u]=1,lc[u]=rc[u]=0,val[u]=v,key[u]=rand();
		return u;
	}
	void Split(int root,int &r1,int &r2,int k){
		if(!root)  {r1=r2=0;return;}
		if(val[root]<=k)  r1=root,Split(rc[root],rc[r1],r2,k);
		else  r2=root,Split(lc[root],r1,lc[r2],k);
		sze[root]=sze[lc[root]]+sze[rc[root]]+1;
	}
	void Merge(int &root,int r1,int r2){
		if(!r1||!r2)  {root=r1+r2;return;}
		if(key[r1]<key[r2])  root=r1,Merge(rc[root],rc[r1],r2);
		else  root=r2,Merge(lc[root],r1,lc[r2]);
		sze[root]=sze[lc[root]]+sze[rc[root]]+1;
	}
	void Insert(int k){
		int r1=0,r2=0;
		int x=newnode(k);
		Split(root,r1,r2,k);
		Merge(r1,r1,x);
		Merge(root,r1,r2);
	}
	void Delete(int k){
		int r1=0,r2=0,r3=0;
		Split(root,r1,r2,k);
		Split(r1,r1,r3,k-1);
		Merge(r3,lc[r3],rc[r3]);
		Merge(r1,r1,r3);
		Merge(root,r1,r2);
	}
	int Rank(int k){
		int r1=0,r2=0;
		Split(root,r1,r2,k-1);
		int ans=sze[r1]+1;
		Merge(root,r1,r2);
		return ans;
	}
	int find(int root,int k){
		if(!root)  return 0;
		if(k==sze[lc[root]]+1)  return root;
		if(k<sze[lc[root]]+1)  return find(lc[root],k);
		return find(rc[root],k-sze[lc[root]]-1);
	}
	int Val(int x)  {return val[find(root,x)];}
	int Prefix(int x){
		int r1=0,r2=0;
		Split(root,r1,r2,x-1);
		int pos=find(r1,sze[r1]);
		Merge(root,r1,r2);
		return val[pos];
	}
	int Suffix(int x){
		int r1=0,r2=0;
		Split(root,r1,r2,x);
		int pos=find(r2,1);
		Merge(root,r1,r2);
		return val[pos];
	}
}
int main(){
	n=in();
	srand(time(0));
	while(n--){
		int op=in(),x=in();
		if(op==1)  FHQ::Insert(x);
		else  if(op==2)  FHQ::Delete(x);
		else  if(op==3)  printf("%d\n",FHQ::Rank(x));
		else  if(op==4)  printf("%d\n",FHQ::Val(x));
		else  if(op==5)  printf("%d\n",FHQ::Prefix(x));
		else  printf("%d\n",FHQ::Suffix(x));
	}
	return 0;
}

LCT

source:「洛谷 3690」Link Cut Tree

#include
using namespace std;
namespace IO{
	const int Rlen=1<<22|1;
	char buf[Rlen],*p1,*p2;
	inline char gc(){
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	template<typename T>
	inline T Read(){
		char c=gc();T x=0,f=1;
		while(!isdigit(c))  f^=(c=='-'),c=gc();
		while( isdigit(c))  x=((x+(x<<2))<<1)+(c^48),c=gc();
		return f?x:-x;
	}
	inline int in()  {return Read<int>();}
}
using IO::in;
const int N=1e5+5;
int n,m;
namespace LCT{
	int fa[N],son[N][2],Xor[N],val[N],mark[N];
	inline bool Get(int x)  {return son[fa[x]][1]==x;}
	inline bool isroot(int x)  {return (!fa[x])||(son[fa[x]][0]!=x&&son[fa[x]][1]!=x);}
	inline void pushup(int x)  {Xor[x]=Xor[son[x][0]]^Xor[son[x][1]]^val[x];}
	inline void pushdown(int x){
		if(!mark[x])  return;
		swap(son[x][0],son[x][1]);
		if(son[x][0])  mark[son[x][0]]^=1;
		if(son[x][1])  mark[son[x][1]]^=1;
		mark[x]=0;
	}
	inline void Rotate(int x){
		int y=fa[x],z=fa[y],k=Get(x),l=son[x][k^1];
		son[y][k]=l,fa[l]=(l?y:0);
		if(!isroot(y))  son[z][Get(y)]=x;fa[x]=z;
		son[x][k^1]=y,fa[y]=x;
		pushup(y),pushup(x);
	}
	int stk[N],top;
	inline void Splay(int x){
		stk[top=1]=x;
		for(int i=x;!isroot(i);i=fa[i])  stk[++top]=fa[i];
		while(top)  pushdown(stk[top]),top--;
		while(!isroot(x)){
			int y=fa[x];
			if(!isroot(y))  Rotate(Get(x)==Get(y)?y:x);
			Rotate(x);
		}
	}
	inline void Access(int x){
		for(int i=0;x;x=fa[i=x])
			Splay(x),son[x][1]=i,pushup(x);
	}
	inline int Findroot(int x){
		Access(x),Splay(x);
		while(pushdown(x),son[x][0])  x=son[x][0];
		return Splay(x),x;
	}
	inline void Makeroot(int x)  {Access(x),Splay(x),mark[x]^=1;}
	inline void Path(int x,int y)  {Makeroot(x),Access(y),Splay(y);}
	inline void Link(int x,int y){
		if(Findroot(x)==Findroot(y))  return;
		Makeroot(x),fa[x]=y;
	}
	inline void Cut(int x,int y){
		if(Findroot(x)!=Findroot(y))  return;
		Path(x,y);
		if(!fa[x]||son[x][1])  return;
		fa[x]=0,son[y][0]=0,pushup(y);
	}
}
int main(){
	n=in(),m=in();
	for(int i=1;i<=n;++i)  LCT::val[i]=in();
	while(m--){
		int op=in(),x=in(),y=in();
		if(!op)  LCT::Path(x,y),printf("%d\n",LCT::Xor[y]);
		else  if(op==1)  LCT::Link(x,y);
		else  if(op==2)  LCT::Cut(x,y);
		else  LCT::Splay(x),LCT::val[x]=y,LCT::pushup(x);
	}
	return 0;
}

字符串

AC 自动机

source:「洛谷 5357」AC自动机

感觉是挺好的一道模板题。

AC 自动机上每匹配到一个节点,实际上从 fail 到根都能被匹配到一次。因此先把 fail 树建出来,链加就用差分维护,由于都是到根的链加,因此我们只用在当前点打个标记即可。

注意读入的串有相同的。

#include
using namespace std;
namespace IO{
	const int Rlen=1<<22|1;
	char buf[Rlen],*p1,*p2;
	inline char gc(){
		return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
	}
	template<typename T>
	inline T Read(){
		char c=gc();T x=0,f=1;
		while(!isdigit(c))  f^=(c=='-'),c=gc();
		while( isdigit(c))  x=(x+(x<<2)<<1)+(c^48),c=gc();
		return f?x:-x;
	}
	inline int gi()  {return Read<int>();}
	inline int gs(char *S){
		char c=gc();int len=0;
		while(!isalpha(c))  c=gc();
		while( isalpha(c))  S[++len]=c,c=gc();
		return S[len+1]='\0',len;
	}
}
using IO::gi;
using IO::gs;
const int N=2e5+5;
int n,l,tot,End[N],Tag[N];
struct Trie{
	int fail,son[26];
}a[N];
char S[N*10];
int t,first[N],v[N],nxt[N];
void add(int x,int y)  {nxt[++t]=first[x],first[x]=t,v[t]=y;}
void Insert(int id,char *S){
	int p=0;
	for(int i=1;i<=l;++i){
		if(!a[p].son[S[i]-'a'])
			a[p].son[S[i]-'a']=++tot;
		p=a[p].son[S[i]-'a'];
	}
	End[id]=p;
}
queue<int>Q;
void Get_fail(){
	for(int i=0;i<26;++i)
		if(a[0].son[i])  Q.push(a[0].son[i]);
	while(!Q.empty()){
		int x=Q.front();Q.pop();
		add(a[x].fail,x);
		for(int i=0;i<26;++i){
			if(a[x].son[i])  a[a[x].son[i]].fail=a[a[x].fail].son[i],Q.push(a[x].son[i]);
			else  a[x].son[i]=a[a[x].fail].son[i];
		}
	}
}
void Work(){
	int p=0;
	for(int i=1;i<=l;++i){
		p=a[p].son[S[i]-'a'],Tag[p]++;
	}
}
void dfs(int x){
	for(int i=first[x];i;i=nxt[i]){
		int to=v[i];
		dfs(to),Tag[x]+=Tag[to];
	}
}
int main(){
	n=gi();
	for(int i=1;i<=n;++i)  l=gs(S),Insert(i,S);
	Get_fail(),l=gs(S),Work(),dfs(0);
	for(int i=1;i<=n;++i)  printf("%d\n",Tag[End[i]]);
	return 0;
}

SAM

source:「洛谷 3804」后缀自动机

到现在打 SAM 还是觉得好妙啊。

#include
using namespace std;
typedef long long ll;
const int N=2e6+5;
int n,sum[N],Sort[N];
char S[N];
template<typename T>void Max(T &x,T y)  {if(x<y)x=y;}
template<typename T>void Min(T &x,T y)  {if(x>y)x=y;}
namespace SAM{
	int last=1,tot=1;
	struct node{
		int fa,len,sze,nxt[26];
	}a[N];
	void Insert(int c){
		int p,cur=++tot;
		a[cur].len=a[last].len+1,a[cur].sze=1;
		for(p=last;p&&!a[p].nxt[c];p=a[p].fa)  a[p].nxt[c]=cur;
		if(!p)  a[cur].fa=1;
		else{
			int now=a[p].nxt[c];
			if(a[now].len==a[p].len+1)  a[cur].fa=now;
			else{
				int Clone=++tot;
				a[Clone]=a[now],a[Clone].len=a[p].len+1,a[Clone].sze=0;
				for(;p&&a[p].nxt[c]==now;p=a[p].fa)  a[p].nxt[c]=Clone;
				a[cur].fa=a[now].fa=Clone;
			}
		}
		last=cur;
	}
}
using namespace SAM;
int main(){
	scanf("%s",S+1),n=strlen(S+1);
	for(int i=1;i<=n;++i)  SAM::Insert(S[i]-'a');
	for(int i=1;i<=tot;++i)  sum[a[i].len]++;
	for(int i=1;i<=tot;++i)  sum[i]+=sum[i-1];
	for(int i=1;i<=tot;++i)  Sort[sum[a[i].len]--]=i;
	ll ans=0;
	for(int i=tot;i>=1;--i){
		int x=Sort[i];
		if(a[x].sze!=1)  Max(ans,(ll)a[x].len*a[x].sze);
		a[a[x].fa].sze+=a[x].sze;
	}
	printf("%lld\n",ans);
	return 0;
}

PAM

source:「洛谷 5496」回文自动机

快忘完了,复习一下。

#include
using namespace std;
const int N=5e5+5;
namespace PAM{
	int n,tot,last,S[N],fail[N],len[N],num[N],sze[N],son[N][26];
	void init()  {tot=1,S[0]=-1,fail[0]=fail[1]=1,len[0]=0,len[1]=-1;}
	int Getfail(int x)  {while(S[n]!=S[n-len[x]-1])x=fail[x];return x;}
	void Insert(int c){
		S[++n]=c;
		int p=Getfail(last);
		if(!son[p][c]){
			int now=Getfail(fail[p]);
			fail[++tot]=son[now][c],son[p][c]=tot,len[tot]=len[p]+2;
		}
		last=son[p][c],sze[last]++;
		num[last]=num[fail[last]]+1;
	}
}
int ans=0;
int main(){
	PAM::init();
	char c=getchar();
	while(!isalpha(c))  c=getchar();
	while( isalpha(c)){
		PAM::Insert((c-'a'+ans)%26);
		printf("%d ",ans=PAM::num[PAM::last]);
		c=getchar();
	}
	return 0;
}

数论

exCRT

source:「洛谷 4777」扩展中国剩余定理

这个也快忘了。。。

#include
using namespace std;
typedef long long ll;
int n;
ll mul(ll x,ll y,ll p)  {return (x*y-(ll)((long double)x/p*y)*p+p)%p;}
ll gcd(ll a,ll b)  {return b?gcd(b,a%b):a;}
void exgcd(ll a,ll b,ll &x,ll &y){
	if(!b)  {x=1,y=0;return;}
	exgcd(b,a%b,y,x),y-=a/b*x;
}
int main(){
	ll a1,a2,m1,m2,x,y;
	scanf("%d%lld%lld",&n,&m1,&a1);
	for(int i=2;i<=n;++i){
		scanf("%lld%lld",&m2,&a2);
		ll Gcd=gcd(m1,m2),Lcm=m1/Gcd*m2,A=a2-a1;
		if(A%Gcd)  return puts("-1"),0;
		m1/=Gcd,m2/=Gcd,A/=Gcd;
		exgcd(m1,m2,x,y),x=(mul(x,A,m2)+m2)%m2;
		a1=(a1+m1*Gcd%Lcm*x%Lcm)%Lcm,m1=Lcm;
	}
	printf("%lld\n",a1);
	return 0;
}

BSGS

source:「CQOI 2018」破解D-H协议

unordered_map 换成了哈希表,快的一匹。

#include
using namespace std;
int n,g,P;
int add(int x,int y)  {return x+y>=P?x+y-P:x+y;}
int dec(int x,int y)  {return x-y< 0?x-y+P:x-y;}
int mul(int x,int y)  {return 1ll*x*y>=P?1ll*x*y%P:x*y;}
int power(int a,int b){
	int ans=1;
	for(;b;b>>=1,a=mul(a,a))  if(b&1)  ans=mul(ans,a);
	return ans;
}
namespace Hash_Table{
	const int N=1e6+10,mod=1e6+3;
	int t,first[N],v[N],w[N],nxt[N];
	void Clear()  {t=0,memset(first,0,sizeof(first));}
	void Add(int x,int y,int z)  {nxt[++t]=first[x],first[x]=t,v[t]=y,w[t]=z;}
	void Insert(int x,int val){
		int pos=x%mod;
		for(int i=first[pos];i;i=nxt[i])
			if(v[i]==x)  {w[i]=val;return;}
		Add(pos,x,val);
	}
	int Query(int x){
		int pos=x%mod;
		for(int i=first[pos];i;i=nxt[i])
			if(v[i]==x)  return w[i];
		return -1;
	}
}
using namespace Hash_Table;
int BSGS(int a,int b){
	Clear();
	int now=b,t=ceil(sqrt(P));
	for(int i=0;i<t;++i)  Insert(now,i),now=mul(now,a);
	if(!a)  return b?-1:1;
	a=power(a,t),now=1;
	for(int i=0;i<=t;++i){
		int tmp=Query(now);
		if((~tmp)&&i*t-tmp>=0)  return i*t-tmp;
		now=mul(now,a);
	}
}
int main(){
	scanf("%d%d%d",&g,&P,&n);
	for(int i=1,A,B;i<=n;++i){
		scanf("%d%d",&A,&B);
		int a=BSGS(g,A),b=BSGS(g,B);
		printf("%d\n",power(g,1ll*a*b%(P-1)));
	}
	return 0;
}

高斯消元

source:「洛谷 3389」高斯消元法

听说洛谷的数据比较水。。

建议做几道模板题测一下模板。

#include
using namespace std;
const int N=105;
int n,flag=1;
double a[N][N],X[N];
void Guass(){
	for(int i=1;i<=n;++i){
		int k=i;
		for(int j=i+1;j<=n;++j)
			if(fabs(a[j][i])>fabs(a[k][i]))  k=j;
		swap(a[i],a[k]);
		for(int j=i+1;j<=n;++j)
			for(int k=i+1;k<=n+1;++k)
				a[j][k]-=a[i][k]*a[j][i]/a[i][i];
	}
	for(int i=n;i>=1;--i){
		for(int j=i+1;j<=n;++j)
			a[i][n+1]-=X[j]*a[i][j];
		if(fabs(a[i][i])<1e-8)  flag=0;
		X[i]=a[i][n+1]/a[i][i];
	}
}
int main(){
	scanf("%d",&n);
	for(int i=1;i<=n;++i)
		for(int j=1;j<=n+1;++j)
			scanf("%lf",&a[i][j]);
	Guass();
	if(!flag)  return puts("No Solution"),0;
	for(int i=1;i<=n;++i)
		printf("%.2lf\n",X[i]);
	return 0;
}

二次剩余

source:「洛谷 5491」二次剩余

#include
using namespace std;
typedef pair<int,int> pii;
int n,P;
int add(int x,int y)  {return x+y>=P?x+y-P:x+y;}
int dec(int x,int y)  {return x-y< 0?x-y+P:x-y;}
int mul(int x,int y)  {return 1ll*x*y>=P?1ll*x*y%P:x*y;}
int power(int a,int b){
	int ans=1;
	for(;b;b>>=1,a=mul(a,a))  if(b&1)  ans=mul(ans,a);
	return ans;
}
namespace Cipolla{
	int I,val;
	struct num{
		int x,y;
		num(int x=0,int y=0):x(x),y(y){}
		friend num operator*(const num &a,const num &b){
			return num(add(mul(a.x,b.x),mul(val,mul(a.y,b.y))),add(mul(a.y,b.x),mul(a.x,b.y)));
		}
		friend num operator^(num a,int b){
			num ans(1,0);
			for(;b;b>>=1,a=a*a)  if(b&1)  ans=ans*a;
			return ans;
		}
	};
	pii Sqrt(int n){
		if(P==2)  return pii(n,n);
		if(!n)  return pii(0,0);
		if(power(n,(P-1)>>1)==P-1)  return pii(-1,-1);
		while(1){
			I=rand()%P,val=dec(mul(I,I),n);
			if(power(val,(P-1)>>1)==P-1)  break;
		}
		int x=(num(I,1)^((P+1)>>1)).x;
		return x=(x<P-x)?x:P-x,pii(x,P-x);
	}
}
using namespace Cipolla;
int main(){
	int T;
	scanf("%d",&T);
	while(T--){
		scanf("%d%d",&n,&P),n%=P;
		pii ans=Sqrt(n);
		if(ans.first==-1)  puts("Hola!");
		else  if(ans.first==ans.second)  printf("%d\n",ans.first);
		else  printf("%d %d\n",ans.first,ans.second);
	}
	return 0;
}

你可能感兴趣的:(#,总结)