模板记忆

图论

虚树

对 dfn 排序,然后在中间插入两两 lca,再排序去重。最后求出再求两两 lca 连边。注意第一个点不连。

void build(){
	tot=0;
	sort(h+1,h+1+m,cmp);
	for(int i=1;i<m;i++){
		int Lca=lca(h[i],h[i+1]);
		a[++tot]=h[i]; a[++tot]=Lca;
	}
	a[++tot]=h[m];
	sort(a+1,a+1+tot,cmp);
	tot=unique(a+1,a+1+tot)-a-1;
	for(int i=1;i<tot;i++){
		int Lca=lca(a[i],a[i+1]);
		add(Lca,a[i+1]);
	}
}

tarjan 求 LCA

刚进来打上 vis=1 标记,离开打上 vis=2 标记。搜索到子树的时候,并查集将子树并进去。遍历对于当前节点的询问,如果另一个点已经离开了或者两点相同(注意别忘了),就搜一下并查集。

void tarjan(int x){
	vis[x]=1;
	for(int i=head[x];i;i=edge[i].next){
		int y=edge[i].to;
		if(vis[y]) continue;
		tarjan(y); fa[y]=x;
	}
	for(int i=0;i<query[x].size();i++){
		int y=query[x][i]; int id=query_id[x][i];
		if(vis[y]==2||x==y) ans[id]=find(y);	
	}
	vis[x]=2;
}

dfs序求 LCA

void dfs(int u,int father){
	pa[0][dfn[u]=++tot]=father;
	for(auto v:G[u]) 
		if(v!=father) dfs(v,u);
}
int cmp(int x,int y){ return dfn[x]<dfn[y]?x:y; }
int lca(int u,int v){
	if(u==v) return u;
	u=dfn[u]; v=dfn[v];
	if(u>v) swap(u,v);
	u++; int k=lg[v-u+1];
	return cmp(pa[k][u],pa[k][v-(1<<k)+1]);
}

网络流

bool bfs(int s,int t){
	memset(d,0,sizeof(d));
	while(!q.empty()) q.pop(); 
	d[s]=1; now[s]=head[s]; q.push(s);//清空 now(s) 
	while(!q.empty()){
		int u=q.front();  q.pop();
		for(int i=head[u];i;i=edges[i].Next){
			int v=edges[i].to;
            //注意两个条件
			if(!edges[i].flow||d[v]) continue; 
            //清空 now(v)
			q.push(v); now[v]=head[v]; d[v]=d[u]+1; 
			if(v==t) return 1;
		}
	}
	return 0;
}
long long dinic(int s,int t,long long flow){
	if(s==t) return flow;
	long long rest=flow,k;
	for(int i=now[s];i&&rest;i=edges[i].Next){//rest有剩余 
	  	int v=edges[i].to; now[s]=i;
        //注意d的条件 
	 	if(!edges[i].flow||d[v]!=d[s]+1) continue;
	    k=dinic(v,t,min(rest,edges[i].flow));
		if(!k) d[v]=0;
		edges[i].flow-=k; edges[i^1].flow+=k; rest-=k;
	}
	return flow-rest;
}

树链剖分

long long query_1(int u,int v){
	long long res=0;
	while(top[u]!=top[v]){
		if(depth[top[u]]<depth[top[v]]) swap(u,v);
		res=(res+query(1,id[top[u]],id[u]))%mod;
		u=fa[top[u]];
	}
	if(depth[u]>depth[v]) swap(u,v);
	res=(res+query(1,id[u],id[v]))%mod;
	return res;
}

树哈希

ull f(ull x){ 
  return ((x*x*4243298829+x*432092)/8)^321432309+x*43243290;
}
ull dfs(int u,int fa){
	vector<ull> h;
	for(int i=0;i<G[u].size();i++){
		int v=G[u][i];
		if(v==fa) continue;
		h.push_back(dfs(v,u));
	}
	ull ret=1;
	for(int i=0;i<(int)h.size();i++) ret+=f(h[i]);
	return ret;
}

tarjan

边双

如果要缩点就遍历所有边,当种类不同时连边。

void tarjan(int u,int in_edge){
	dfn[u]=low[u]=++cnt;
	for(int i=head[u];i;i=edges[i].Next){
		int v=edges[i].to;
		if(!dfn[v]){
			tarjan(v,i);
			low[u]=min(low[u],low[v]);
			if(low[v]>dfn[u])
				bridge[i]=bridge[i^1]=1;
		}else if(i!=(in_edge^1)) low[u]=min(low[u],dfn[v]);
	}
}
void dfs(int u){
	c[u]=dcc; s[dcc].push_back(u);
	for(int i=head[u];i;i=edges[i].Next){
		int v=edges[i].to;
		if(c[v]||bridge[i]) continue;
		dfs(v); 
	}
}

点双

如果要缩点的话,给每个割点赋一个编号(顺着 V-DCC 的编号)。然后遍历每一个 V-DCC,找到其中的割点,然后连边割点和 V-DCC。

void tarjan(int u){
	dfn[u]=low[u]=++cnt; st[++top]=u; int flag=0;
	if(u==root&&head[u]==0){
		Dcc[++dcc].push_back(u); return ; 
	}
	for(int i=head[u];i;i=edges[i].Next){
		int v=edges[i].to;
		if(!dfn[v]){
			tarjan(v);
			low[u]=min(low[u],low[v]);
			if(low[v]>=dfn[u]){
				flag++; dcc++; int z;
				if(u!=root||flag>1) cut[u]=1;
				do{
					z=st[top--];
					Dcc[dcc].push_back(z); 
				}while(z!=v);
				Dcc[dcc].push_back(u); 
			}
		}else low[u]=min(low[u],dfn[v]);
	}
}

动态规划

按层扩展

void prework(){
	memset(road,inf,sizeof(road));
	for(int i=0;i<(1<<n);i++){
		expand[i]=i;
		for(int j=1;j<=n;j++){
			if(i>>(j-1)&1){
				road[i][j]=0;
				for(int k=1;k<=n;k++){
					if(a[j][k]>=inf) continue;
					expand[i]|=1<<(k-1);
					road[i][k]=min(road[i][k],a[j][k]);
				}
			}
		}
	}
	for(int i=0;i<(1<<n);i++){
		for(int j=(i-1)&i;j;j=(j-1)&i){
			if((i&j)==j&&(i&expand[j])==i){
				valid[i].push_back(j);
				int sum=0;
				for(int k=1;k<=n;k++)
					if((j^i)>>(k-1)&1) sum+=road[j][k]; 
				cost[i].push_back(sum); 
			}
		}
	}
	return ;
}

树上背包

重新动态计算 Size,第一层枚举卡住上届,第二层枚举上下界都要卡住。

	Size[u]=1;
	for(int i=0;i<G[u].size();i++){
		int v=G[u][i];
		if(v==fa) continue;
		Dp(v,u); Size[u]+=Size[v];
		for(int k=min(Size[u],m);k>=1;k--)
			for(int x=max(0,k-Size[u]+Size[v]);x<=min(k,Size[v]);x++)
	}

数位

limit 记录前导 \(0\) 限制,lead 为是否贴着上界。如果二者都为 \(0\) 才能记录进 vis。

long long dfs(bool limit,bool lead,int pos,int num,int sum){
	if(pos==0&&sum==0) return 0;
	if(pos==0) return (sum==mod)&&(num==0);
	if(vis[pos][num][sum]&&!limit&&!lead) return f[pos][num][sum]; 
	int up=limit?a[pos]:9; long long res=0;
	for(int i=0;i<=up;i++)
		res+=dfs(limit&(i==a[pos]),lead&(i==0),pos-1,(num*10+i)%mod,sum+i);
	if(!limit&&!lead) vis[pos][num][sum]=1,f[pos][num][sum]=res;
	return res;
}

斜率优化

	int l,r; l=r=1; q[l]=0; dp[0]=0;
	for(int i=1;i<=n;i++){
		while(l<r&&slope(q[l],q[l+1])>(double)K(i)) l++;
		int j=q[l];
		dp[i]=dp[j]+A(i,j)+B(i);
		while(l<r&&slope(q[r-1],q[r])<=slope(q[r],i)) r--;  
		q[++r]=i;
	}

CDQ解斜率优化

先对时间序排,然后左右各按横坐标和斜率排一遍

void cdq(int l,int r){
    if(l==r){ dp[l]=max(dp[l],dp[l-1]); return;}
    sort(s+l,s+r+1);
    int mid=(l+r)>>1;
    cdq(l,mid);
    sort(s+l,s+mid+1,cmpX);
    sort(s+mid+1,s+r+1,cmpK);
    int ll=1,rr=0;
    for(int i=l;i<=mid;i++){
        while(ll<rr&&slope(q[rr-1],q[rr])<slope(q[rr],s[i])) rr--;
        q[++rr]=s[i];
	}
    for(int i=mid+1;i<=r;i++){
        while(ll<rr&&k(s[i])<slope(q[ll],q[ll+1])) ++ll;
        dp[s[i]]=max(dp[s[i]],a[s[i]]*x(q[ll])+b[s[i]]*y(q[ll]));
    }   
    cdq(mid+1,r);
}

wqs二分

注意最后要再 check(l) 一遍或者途中就记下合法答案。否则可能在 check(mid) 后答案变动。

二维四边形不等式

         for(int k=p[l][r-1];k<=p[l+1][r];k++){
                int t=dp[l][k]+dp[k+1][r]+s[r]-s[l-1];
                if(dp[l][r]>t){
                    dp[l][r]=t;
                    p[l][r]=k;
                }
            }

决策单调性之队列

void insert(int i,int &l,int &r){
	int w=-1;
	while(l<=r){
		if(calc(q[r].l,i)<=calc(q[r].l,q[r].x)) w=q[r--].l;//队尾整体差于i,弹出队尾 
		else{
			if(calc(q[r].r,q[r].x)>=calc(q[r].r,i)){//队尾前半部分优于i,后半部分差于i 
				int l1=q[r].l,r1=q[r].r;
				while(l1<r1){
					int mid=(l1+r1)>>1;
					if(calc(mid,i)>calc(mid,q[r].x)) l1=mid+1;
					else r1=mid;
				}
				q[r].r=l1-1; w=l1;
			}
			break;
		}
	}
	if(w!=-1) q[++r]=(Q){w,n,i};
}
	for(int i=1;i<=n;i++){
		int j=q[l].x;
		f[i]=calc(i,j);
		pre[i]=j;
		while(l<=r&&q[l].r<=i) l++;//排除无用部分p[1~i-1] 
		q[l].l=i+1;
		insert(i,l,r);
	}

矩阵写法

struct Matrix{
    ll e[3][3];
    ll& operator () (int a,int b) { return e[a][b]; }
}M;
Matrix operator * (Matrix a,Matrix b){
   	Matrix c;
	for(int i=0;i<=2;i++)
		for(int j=0;j<=2;j++) c(i,j)=inf;
	for(int i=0;i<=2;i++)
		for(int j=0;j<=2;j++)
			for(int k=0;k<=2;k++) c(i,j)=min(c(i,j),a(i,k)+b(k,j));
   	return c;
}

动态 dp

第二次 dfs 动态维护全局矩阵与 \(f\)
修改时,先改全局矩阵值,然后 pushup 修改平衡树矩阵值。
最后如果父亲在同一颗平衡树内直接 pushup。
不同平衡树内说明链变化了,于是先让父亲(在另一条重链上)的全局矩阵减去旧的 \(f\) 值,再更新 \(top\)\(f\) 值,最后让父亲的全局矩阵加上新的 \(f\) 值。
`

			M[fa[u]].mat[0][0]-=max(f[top[u]][0],f[top[u]][1]); M[fa[u]].mat[1][0]-=f[top[u]][0];
			f[top[u]][0]=max(mul[u].mat[0][0],mul[u].mat[0][1]); f[top[u]][1]=max(mul[u].mat[1][0],mul[u].mat[1][1]);
			M[fa[u]].mat[0][0]+=max(f[top[u]][0],f[top[u]][1]); M[fa[u]].mat[1][0]+=f[top[u]][0];
			M[fa[u]].mat[0][1]=M[fa[u]].mat[0][0];
			pushup(u=fa[u]);	}
}

`

数学

线性逆元

\(inv_i=(p-p/i)*inv_{p\bmod i} \bmod p\)

矩阵

void mul1(ll A[maxn],ll B[maxn][maxn]){
	ll c[maxn]; memset(c,0,sizeof(c));
	for(int j=1;j<=3;j++)
		for(int k=1;k<=3;k++) c[j]=(c[j]+A[k]*B[k][j])%mod;
	memcpy(A,c,sizeof(c));
}
void mul2(ll A[maxn][maxn]){
	ll c[maxn][maxn]; memset(c,0,sizeof(c));
	for(int i=1;i<=3;i++)
		for(int j=1;j<=3;j++)
			for(int k=1;k<=3;k++) c[i][j]=(c[i][j]+A[i][k]*A[k][j])%mod;
	memcpy(A,c,sizeof(c));
}

高精度

struct A{
	int a[maxl],len;
	inline A operator * (const A x) const {
		A ans;
		memset(ans.a,0,sizeof(ans.a));
		for(int i=1;i<=len;i++)
			for(int j=1;j<=x.len;j++){
				ans.a[i+j-1]+=a[i]*x.a[j];
				ans.a[i+j]+=ans.a[i+j-1]/10;
				ans.a[i+j-1]%=10;
			}
		ans.len=len+x.len-1;
		while(!ans.a[ans.len]&&ans.len>=2) --ans.len;
		if(ans.a[ans.len+1]) ++ans.len;
		return ans;
	}
}n1,n2;
struct A{
	int a[maxl],len;
	inline A operator + (const A x) const {
		A ans;
		memset(ans.a,0,sizeof(ans.a));
		for(int i=1;i<=max(len,x.len);i++){
			ans.a[i]+=a[i]+x.a[i];
			ans.a[i+1]=ans.a[i]/10;
			ans.a[i]%=10;
		}
		ans.len=max(len,x.len);
		if(ans.a[ans.len+1]) ++ans.len;
		return ans;
	}
}n1,n2;

struct A{
	int a[maxl],len;
	inline A operator - (const A x) const {
		A ans;
		memset(ans.a,0,sizeof(ans.a));
		ans.len=0;
		long long num=0;
		for(int i=1;i<=len;i++){
			ans.a[i]+=a[i]-x.a[i];
			if(ans.a[i]<0) ans.a[i]+=10,ans.a[i+1]-=1;
		}
		ans.len=len;
		while(!ans.a[ans.len]&&ans.len>=2) ans.len--;
		return ans;
	}
}n1,n2;
struct A{
	int a[maxl],len;
	inline A operator / (const long long x) const {
		A ans;
		memset(ans.a,0,sizeof(ans.a));
		ans.len=0;
		long long num=0;
		for(int i=len;i>=1;i--){
			num=num*10+a[i];
			ans.a[i]=num/x;
			num%=x;
			if(!ans.len&&ans.a[i]) ans.len=i;
		}
		if(!ans.len) ans.a[++ans.len]=0;
		return ans;
	}
}n1;

数据结构

树套树

void add(int p,int v,int x){
	insert(root[p],x);
	if(l(p)==r(p)) return ;
	int mid=(l(p)+r(p))>>1;
	if(v<=mid) add(p*2,v,x);
	else add(p*2+1,v,x);
}
int query(int p,int l,int r,int k){
	if(l(p)==r(p)) return l(p);
	int Lc=p*2,Rc=p*2+1;
	int cnt=getrank(root[Lc],r)-getrank(root[Lc],l-1);
	if(cnt>=k) return query(Lc,l,r,k);
	else return query(Rc,l,r,k-cnt);
}

李超线段树

double val(int num,int x){ return a[num]+d[num]*(x-1); }
void update(int id){
	int p=1;
	while(p<=n){
		if(val(id(p),l(p))<val(id,l(p))&&val(id(p),r(p))<val(id,r(p))){
			id(p)=id; break;
		}
		if(val(id(p),l(p))>val(id,l(p))&&val(id(p),r(p))>val(id,r(p))) break;
		int mid=(l(p)+r(p))>>1;
		if(val(id(p),mid)<val(id,mid)) swap(id,id(p));
		if(d[id(p)]>d[id]) p=lc;
		else p=rc;
	}
}
double query(int y){
	int p=1; double ans=-1e9;
	while(p<=n){
		ans=max(ans,val(id(p),y));
		int mid=(l(p)+r(p))>>1;
		if(y<=mid) p=lc;
		else p=rc;
	}
	if(ans==-1e9) ans=0;
	return ans;
}

笛卡尔树

编号满足二叉搜索树,权值满足小根堆。
注意中间用 \(k\) 替代 \(top\),记录 root

	for(int i=1;i<=n;i++){
		int k=top;
		while(k&&p[i]<p[st[k]]) k--;
		if(k) rs[st[k]]=i;
		else root=i;
		if(k<top) ls[i]=st[k+1];
		top=k; st[++top]=i;
	}

CDQ分治

inline void cdq(int l,int r){
	if(l>=r) return ;
	int mid=(l+r)>>1; 
	cdq(l,mid); cdq(mid+1,r);
	//别忘记 cmp2 
	sort(b+l,b+mid+1,cmp2);
	sort(b+mid+1,b+r+1,cmp2);
	register int j=l;
	for(register int i=mid+1;i<=r;i++){
		while(b[i].y>=b[j].y&&j<=mid){//第二维 
			update(b[j].z,b[j].cnt);//第三维 
			j++;
		}
		b[i].ans+=query(b[i].z);
	}
	for(register int i=l;i<j;i++) update(b[i].z,-b[i].cnt);
}

KDT

int build(int l,int r){
	if(l>r) return 0;
	int p=++tot,mid=(l+r)>>1;
	l(p)=l; r(p)=r;
	for(int i=0;i<maxd;i++) Max[p][i]=-inf,Min[p][i]=inf;
	for(int i=l;i<=r;i++){
		for(int j=0;j<maxd;j++){
			Max[p][j]=max(Max[p][j],d[c[i]][j]);
			Min[p][j]=min(Min[p][j],d[c[i]][j]); 
		}
	}
	int D=find(l,r);
	nth_element(c+l,c+mid,c+r+1,cmp);
	id(p)=c[mid];
	lc(p)=build(l,mid-1);
	rc(p)=build(mid+1,r);
	return p;
}
long long maxdist(int u){
	long long dx=max(abs(x0-Max[u][0]),abs(x0-Min[u][0]));
	long long dy=max(abs(y0-Max[u][1]),abs(y0-Min[u][1]));
	return dx*dx+dy*dy;
}
bool maybe(int u){ return u&&(q.size()<k0||maxdist(u)>=q.top().len); }
void dfs(int p){
	long long Di=dis(x0,y0,d[id(p)][0],d[id(p)][1]);
	if(q.size()<k0) q.push((node){id(p),Di});
	else if((node){id(p),Di}<q.top()){
		q.pop(); q.push((node){id(p),Di});
	}
	if(maxdist(lc(p))>maxdist(rc(p))){
		if(maybe(lc(p))) dfs(lc(p));
		if(maybe(rc(p))) dfs(rc(p)); 
	}else{
		if(maybe(rc(p))) dfs(rc(p));
		if(maybe(lc(p))) dfs(lc(p));
	}
}

边带权并查集

int find(int x){
	if(fa[x]==x) return x;
	int pa=find(fa[x]);
	dep[x]+=dep[fa[x]];
	return fa[x]=pa;
}
void merge(int x,int y){
	fa[x]=y; dep[x]=size[y]; size[y]+=size[x];
}

可并堆

    struct Edge{
        int cnt,head[maxn],Next[maxn],to[maxn],s[maxn],top;
        int allocate(){
            if(top) return s[top--];
            else return ++cnt;
        }
        void recycle(int u){
            s[++top]=u;
        }
        void push(int u,int v){
            int o=allocate();
            Next[o]=head[u];
            to[o]=v;
            head[u]=o;
        }
    }E;
    int root[maxn],fa[maxn],q[maxn<<1];
    ll val[maxn],add[maxn],mul[maxn];
	ll pushdown(int u){
		if(mul[u]==1&&add[u]==0) return val[u];
        for(int i=E.head[u];i;i=E.Next[i]){
    		int v=E.to[i];
			mul[v]*=mul[u];
            add[v]*=mul[u];
			add[v]+=add[u];
		}
		val[u]*=mul[u]; val[u]+=add[u];
		mul[u]=1; add[u]=0;
		return val[u];
    }
    int merge(int u,int v){
		if(!u||!v) return u+v;
		pushdown(u); pushdown(v);
		if(val[u]>val[v]) swap(u,v);
		E.push(u,v); fa[v]=u;
		return u;
	}
    int pop(int u){
        int l=1,r=0; q[1]=0;
        pushdown(u);
        for(int i=E.head[u];i;i=E.Next[i]){
            int v=E.to[i];
            q[++r]=v;
            E.recycle(i);
        }
        while(l+1<=r){
			q[++r]=merge(q[l],q[l+1]);
			l+=2;
		}
		return q[l];
	}

树状数组倍增

inline int get(int x){
    int l=log2(K),sum=0,Ans=0;
    for(int i=l;i>=0;i--){
        int k=(1<<i);
        if(Ans+k<=K&&sum+c[Ans+k]<x) sum+=c[Ans+k],Ans+=k;
	}
    return Ans+1;
}

可持久化字典树

注意判断 if(v) 再连另一条边

struct Trie{
	void insert(int bh,int len,int v,int u){
		if(len<0){ latest[u]=bh; return ; }
		int ch=pre[bh]>>len&1;
		if(v) tree[u][ch^1]=tree[v][ch^1];
		tree[u][ch]=++cnt;
		insert(bh,len-1,tree[v][ch],tree[u][ch]);
		latest[u]=max(latest[tree[u][0]],latest[tree[u][1]]);
	}
};

fhq

void pushup(int p){ s(p)=s(ls(p))+s(rs(p))+1; }
int New(int val){
	int p=++tot;
	s(p)=1; ls(p)=rs(p)=0;
	val(p)=val; r(p)=rand();
	return p; 
}
void split(int p,int k,int& x,int& y){
	if(!p){ x=y=0; return ;	 }	
	if(val(p)<=k){
		x=p;
		split(rs(p),k,rs(p),y); 
	}else{
		y=p;
		split(ls(p),k,x,ls(p));
	}
	pushup(p);
}
int merge(int p,int q){
	if(!p||!q) return p+q;
	if(r(p)<r(q)){
		ls(q)=merge(p,ls(q)); 
		pushup(q);
		return q;
	}else{
		rs(p)=merge(rs(p),q);
		pushup(p);
		return p;
	}
}
void insert(int v){
	int x=0,y=0;
	split(root,v-1,x,y);
	root=merge(merge(x,New(v)),y);
}
void del(int v){
	int x=0,y=0,z=0;
	split(root,v,x,z); split(x,v-1,x,y);
	root=merge(merge(x,merge(ls(y),rs(y))),z);
}
int kth(int k){
	int p=root;
	while(1){
		if(k<=s(ls(p))) p=ls(p);
		else if(k==s(ls(p))+1) return val(p);
		else k-=s(ls(p))+1,p=rs(p);
	}
}
int pre(int v){
	int p=root,ans=0;
	while(1){
		if(!p) return ans;
		else if(v<=val(p)) p=ls(p);
		else ans=val(p),p=rs(p);
	}
}
int suf(int v){
	int p=root,ans=0;
	while(1){
		if(!p) return ans;
		else if(v>=val(p)) p=rs(p);
		else ans=val(p),p=ls(p);
	}
}
int rnk(int v){
	int x=0,y=0,ans;
	split(root,v-1,x,y); ans=s(x)+1;
	root=merge(x,y); return ans;
}

其他

模拟退火

注意先构造一个较优的解

	double t=3000,deta=0.997;
	while(t>1e-15){
		if(clock()/CLOCKS_PER_SEC>0.95){
			printf("%.3lf %.3lf",Ax,Ay);
			exit(0);
		}
		double x=ansx+(rand()*2-RAND_MAX)*t,y=ansy+(rand()*2-RAND_MAX)*t;
		double E=calc(x,y);
		double D1=E-answ,D2=E-Aw;
    	if(D1<0) ansx=x,ansy=y,answ=E;
    	if(D2<0) Ax=x,Ay=y,Aw=E;
      	if(exp(-D1/t)*RAND_MAX>rand()) ansx=x,ansy=y;
		t*=deta;
	}
		double limit=CLOCKS_PER_SEC*0.995;
		if(clock()>limit){
			cout<<ans;
			exit(0);
		}
		int x=random(n-1)+1,y=random(n-1)+1;
		swap(d[x],d[y]);
		long long A=calc();
		if(ans>A) ans=A;
		else if(exp((ans-A)/t)*RAND_MAX<rand()) swap(d[x],d[y]);
		t*=kt;

字符串

KMP

	Next[1]=0; int j=0;
	for(int i=2;i<=m;i++){//每次进入时,j=Next[i-1] 
		while(j&&p[i]!=p[j+1]) j=Next[j];
		if(p[i]==p[j+1]) j++;
		Next[i]=j;
	}

SAM

两倍数组,last=tot=1; !p 的时候父亲设置为 \(1\) 而非 \(0/p\)

void add(int c){
	int p=last; int np=last=++tot;
	len[np]=len[p]+1; sum[np]=1;
	for(;p&&!ch[p][c];p=fa[p]) ch[p][c]=np;
	if(!p) fa[np]=1;
	else{
		int q=ch[p][c];
		if(len[q]==len[p]+1) fa[np]=q;
		else{
			int nq=++tot;  memcpy(ch[nq],ch[q],sizeof(ch[q]));
			fa[nq]=fa[q]; len[nq]=len[p]+1;
			fa[q]=fa[np]=nq;
			for(;p&&ch[p][c]==q;p=fa[p]) ch[p][c]=nq;
		}
	}
}

最小表示

	int i=1,j=2,k;
	while(i<=n&&j<=n){
		for(k=0;k<n&&a[i+k]==a[j+k];k++);
		if(k==n) break;
		if(a[i+k]>a[j+k]){
			i=i+k+1;
			if(i==j) i++;
		}else{
			j=j+k+1;
			if(i==j) j++; 
		}
	}
	int ans=min(i,j);

ACAM

	void insert(int v){
		if(ms.find(string(p+1))!=ms.end()) return ;
		int len=strlen(p+1),u=0;
		for(int i=1;i<=len;i++){
			int c=idx(p[i]);
			if(!ch[u][c]) ch[u][c]=++tot;
			u=ch[u][c];
		}
		ms[string(p+1)]=v;
		val[u]=v;
	}
	void getfail(){
		queue<int> q; f[0]=0;
		for(int i=1;i<=26;i++){
			int u=ch[0][i];
			if(!u) continue;
			f[u]=last[u]=0; q.push(u);
		}
		while(!q.empty()){
			int r=q.front(); q.pop();
			for(int i=1;i<=26;i++){
				int u=ch[r][i];
				if(!u){ ch[r][i]=ch[f[r]][i]; continue; }
				q.push(u);
				f[u]=ch[f[r]][i];
				last[u]=val[f[u]]?f[u]:last[f[u]];
				if(last[u]) deg[last[u]]++;
			}
		}
	}
posted @ 2024-01-12 21:57  Mirasycle  阅读(11)  评论(0编辑  收藏  举报