各种模板复习及记忆口诀(持续更新)

发布时间 2023-09-14 14:35:15作者: 铃狐sama

模板大复习


本博客适用于复习各种常见模板并且提供记忆方法。
同时本博客也有很多以前的复习或者学习链接,不会或忘记时值得一看。

字符串:

manacher找回文

https://www.luogu.com.cn/problem/P3805

整体翻倍夹#号,R小的为min(R-i,p_2*pos-i),两边拓展不越界,记得更新R上界。

int mana(){
	int len=s.size()-1;
	for(int i=1;i<=len;i++){
		ss[i*2-1]='#';
		ss[i*2]=s[i];
	}
	ss[len*2+1]='#';
	
	int R=0;
	int pos=0;
	len=2*len+1; 
	for(int i=1;i<=len;i++){
		if(i<R){
			p[i]=min(R-i,p[2*pos-i]);
		}
		else{
			p[i]=1;
		}
		
		while(i-p[i]>=1&&i+p[i]<=len&&(ss[i-p[i]]==ss[i+p[i]])){
			p[i]++;
		}
		if(i+p[i]>R){
			pos=i;
			R=i+p[i];
		}
	}
	int ret=0;
	for(int i=1;i<=len;i++){
		ret=max(ret,p[i]-1);
	}
	return ret;
}

重点:kmp找broader

https://www.luogu.com.cn/problem/P3375

(从0开始)
初值即为前者值,两端不等j-1前跳,跳到相等值++,记录状态不要忘。

void kmp(){
	len=s.size();
	int j;
	for(int i=1;i<len;i++){
		j=p[i-1];
		while(s[i]!=s[j]&&j>0){
			j=p[j-1];
		}
		if(s[i]==s[j]){
			j++;
		}
		p[i]=j;
	}
	
}

图论相关:

温馨提示:注意看好边是有向还是无向!

floyd全源最短路

初始化定不能忘,一层k要最外套,里面两层随便跑,用k作为中转点,只要有小就更新。

void floyd(){
	for(int k=1;k<=n;k++){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				dis[i][j]=min(dis[i][j],dis[i][k]+dis[k][j]);
			}
		}
	}
}

djkstra最短路:

https://www.luogu.com.cn/problem/P4779
优先队列来优化,没被访问才进行,访问不会改回0,两个if套一块,外面更新答案,里面添加元素。
稍微注意下重边自环等判断,过了。


struct nod{
	int id;
	int dis;
	bool operator <(const nod &a)const{
		return dis>a.dis;
	}
};
priority_queue<nod>q;
bool vis[200005];
void djkstra(int ss){
	for(int i=1;i<=n;i++){
		dis[i]=1e18;
		vis[i]=0;
	}
	dis[ss]=0;
	q.push((nod){ss,0});
	while(q.size()){
		nod tp=q.top();
		q.pop();
		int x=tp.id;
		if(vis[x]){
			continue;
		}
		vis[x]=1;
		for(int i=0;i<mp[x].size();i++){
			int to=mp[x][i].to;
			int val=mp[x][i].val;
			if(dis[x]+val<dis[to]){
				dis[to]=dis[x]+val;
				if(!vis[to]){
					q.push((nod){to,dis[to]});
				}
			}
			
		}
	}
	
}

SPFA最短路

https://www.luogu.com.cn/problem/P3371
入队标记出队消,两层if不能忘,外面更新答案,不在队加元素,超n次有负环。

void spfa(int ss){
	for(int i=1;i<=n;i++){
		dis[i]=2147483647;
	}
	dis[ss]=0;
	q.push(ss);
	vis[ss]=1;
	while(q.size()){
		int x=q.front();
		q.pop();
		vis[x]=0;
		cnt[x]++;
		for(int i=0;i<mp[x].size();i++){
			int to=mp[x][i].to;
			int val=mp[x][i].val;
			if(dis[x]+val<dis[to]){
				dis[to]=dis[x]+val;
				if(!vis[to]){
					vis[to]=1;
					q.push(to);
				}
			}
		}
		
		if(cnt[x]>=n){
			break;
		}
	}
}

点双:

https://www.luogu.com.cn/problem/P8435
需要注意每次dfs前的清空,无向边。

三数组三常数,更新未更新点,更新时求答案,外部dfn内部low取小。自身起点无儿子特判有,自身开始1儿子非切点。

void dfs(int x,int fa){
	tt++;
	dfn[x]=low[x]=tt;
	s[++top]=x;
	int son=0;
	for(int i=0;i<mp[x].size();i++){
		int to=mp[x][i];
		if(to==fa){
			continue;
		}
		if(!dfn[to]){
			son++;
			dfs(to,x);
			low[x]=min(low[x],low[to]);
			if(dfn[x]<=low[to]){
				cnt++;
				while(s[top+1]!=to){
					bcc[cnt].push_back(s[top]);
					top--;
				}
				bcc[cnt].push_back(x);
			}
		}
		low[x]=min(low[x],dfn[to]);
	}
	if(son==0&&fa==x){
		cnt++;
		bcc[cnt].push_back(x);
	}
}

边双:

https://www.luogu.com.cn/problem/P8436
需要注意重编情况的特判。

(相比点双)
判断拉到最外层,判断条件变相等,特判全部都删除。

void dfs(int x,int fa){
	tt++;
	dfn[x]=low[x]=tt;
	s[++top]=x;
	bool f=0;
	for(int i=0;i<mp[x].size();i++){
		int to=mp[x][i];
		if(to==fa&&f==0){
			f=1;
			continue;
		}
		if(!dfn[to]){
			dfs(to,x);
			low[x]=min(low[x],low[to]);
		}
		low[x]=min(low[x],dfn[to]);
	}
	if(dfn[x]==low[x]){
		cnt++;
		while(s[top+1]!=x){
			bcc[cnt].push_back(s[top]);
			top--;
		}
	}
}

差分约束:

详情:https://www.cnblogs.com/linghusama/p/17542288.html

为了好记:求最值反着约束,反着连边,反着求路径
例如:求最小,约束>=,连边j,i,找最长路。

二分图最大匹配/最小覆盖。

(当然也可以最大流解决,还是要注意下重边)
https://www.luogu.com.cn/problem/P3386

n次外层不能少,每次清空vis不能忘,遍历全部未访问可接点,rev无值或可更改则更改。

bool match(int x){
	for(int i=0;i<mp[x].size();i++){
		int to=mp[x][i];
		if(vis[to]){
			continue;
		}
		vis[to]=1;
		if(!rev[to]||match(rev[to])==1){
			rev[to]=x;
			return 1;
		}
	}
	return 0;
}

.....
for(int i=1;i<=n;i++){
		memset(vis,0,sizeof(vis));
		if(match(i)==1){
			ans++;
		}
	}

最大流(dinic)

详情:https://www.cnblogs.com/linghusama/p/17564343.html
说实话,目前就没遇到过这种题,而且这种题限制也多,码量也多。

树型结构:

线段树:

个人觉得...没必要吧

树状数组:

详情:https://www.cnblogs.com/linghusama/p/17455975.html

平衡树(非旋treap)

详情:https://www.cnblogs.com/linghusama/p/17478930.html

笛卡尔树(感觉考的少)

关键是你做题要看出来是这个树。
大不了我线段树nlogn维护,因为他都考你这个树了,我不信他还要O(n)来做

主席树(感觉考的少)

详情:https://www.cnblogs.com/linghusama/p/17697328.html

最小生成树:

krus那个算法就不说了,用并查集排序后乱做
这里主要是用于很多边但点少的prim算法的口诀记忆。

最开始,集合中只有1号节点,然后从1号节点扩展出到所有节点的d值。然后每次将离集合最近的且不在
集合中的点加入集合,让后从这个点往所有点扩展,更新d值,重复以上过程直到所有点都在集合中了。

int prim(){
	for(int i=2;i<=n;i++){
		dis[i]=0x3f3f3f3f;
	}
	int ans=0;
	int tot=0;
	for(int i=0;i<mp[1].size();i++){
		int to=mp[1][i].to;
		int val=mp[1][i].val;
		dis[to]=min(dis[to],val);
		
	}
	while(++tot<=n-1){
		int mii=0x3f3f3f3f;
		vis[now]=1;
		for(int i=1;i<=n;i++){
			if(!vis[i]&&mii>dis[i]){
				mii=dis[i];
				now=i;
			}
		}
		ans+=mii;
		for(int i=0;i<mp[now].size();i++){
			int to=mp[now][i].to;
			int val=mp[now][i].val;
			dis[to]=min(dis[to],val);
		
		}
	}
	return ans;
}