2025 CSP/NOIp 复习

快读

点击查看代码
#define fw fwrite(obuf,p3-obuf,1,stdout)
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<20,stdin),p1==p2)?EOF:*p1++)
#define putchar(x) (p3-obuf<1<<20?(*p3++=(x)):(fw,p3=obuf,*p3++=(x)))
using namespace std;

char buf[1<<20],obuf[1<<20],*p1=buf,*p2=buf,*p3=obuf,str[20<<2];
int read(){
	int x=0;
	char ch=getchar();
	while(!isdigit(ch))ch=getchar();
	while(isdigit(ch))x=(x<<3)+(x<<1)+(ch^48),ch=getchar();
	return x;
}
template<typename T>
void write(T x,char sf='\n'){
	if(x<0)putchar('-'),x=~x+1;
	int top=0;
	do str[top++]=x%10,x/=10;while(x);
	while(top)putchar(str[--top]+48);
	if(sf^'#')putchar(sf);
}

return fw,0;

其实是重新学习(擦汗

1.Manacher

点击查看代码
//manacher板子  求子串最长回文串的长度 
#include<bits/stdc++.h>
using namespace std;
const int maxn=1.1e7+10;
string s;
int pal[2*maxn],ans;
//pal表示以第i个字符为中心的最大回文半径  不包括中心字符本身
//所以长度为奇数or偶数的回文串都可以处理 
string get(string s){
	string t="$#";
	for(int i=0;i<s.size();i++){
		t+=s[i];
		t+="#";
	}
	t+="@";
	return t;
}

void manacher(string s){
	s=get(s);
	int len=s.size();
	int maxr=0,pos=0;
	//pos表示当前回文中心的位置  maxr是当前回文右边界的位置 
	for(int i=1;i<len;i++){
		if(i<maxr) pal[i]=min(pal[2*pos-i],maxr-i+1);
		else pal[i]=1;
		while(s[i+pal[i]]==s[i-pal[i]]) pal[i]++;
		if(pal[i]+i-1>=maxr){
			maxr=pal[i]+i-1;
			pos=i;
		}
		if(ans<pal[i]-1) ans=pal[i]-1;
	}
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>s;
	manacher(s);
	cout<<ans<<'\n';
}

2.ST 表

点击查看代码
//一眼RMQ(区间最值问题) ST表+倍增
//f[i][j]表示区间(i,i+2^j-1)的最大值 
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e5+10;
int n,q,a,b;
int logn[maxn],f[maxn][25],f2[maxn][25];
//对于每个询问a,b,我们把它分为两部分,[l,l+2^s-1]  [r-2^s+1,r]   
//其中s=log[r-l+1] 
/*关于log 怎么进行预处理?
log[1]=0;
log[i]=log[i/2]+1
*/

void pre(){
	logn[1]=0;
	logn[2]=1;
	for(int i=3;i<=2e5+10;i++){
		logn[i]=logn[i/2]+1;
	}
} 
int s,x,y;
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0);
	cout.tie(0);
	cin>>n>>q;
	for(int i=1;i<=n;i++){
		cin>>f[i][0];
//		f2[i][0]=f[i][0];
	}
//	pre();
	for(int j=1;j<=21;j++){
		for(int i=1;i+(1<<j)-1<=n;i++){//1<<j为2^j次方 倍增思想 
			f[i][j]=max(f[i][j-1],f [i+(1<<(j-1))] [j-1]);
		}
	}
	for(int i=1;i<=q;i++){
		cin>>x>>y;
		s=log2(y-x+1);
		int d=max(f[x][s],f[y-(1<<s)+1][s]);
		cout<<d<<"\n";
	}
} 

3.LCA

1)树剖求LCA

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+10;
int n,m,s;
struct edge{
	int to,nxt;
}e[2*maxn];
int head[maxn],edgenum;
void add_edge(int u,int v){
	e[++edgenum].nxt=head[u];
	e[edgenum].to=v;
	head[u]=edgenum;
}

int f[maxn],dep[maxn],siz[maxn],son[maxn],dfn[maxn],cnt;
int top[maxn];
void dfs(int u,int fa){
	siz[u]=1,f[u]=fa;
	dep[u]=dep[fa]+1;
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(v==fa) continue;
		dfs(v,u);
		siz[u]+=siz[v];
		if(siz[v]>siz[son[u]]) son[u]=v;
	}
}
void dfs2(int u,int t){
	top[u]=t;
	dfn[u]=++cnt;
	if(!son[u]) return;
	if(son[u]) dfs2(son[u],t);
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(v==f[u] || v==son[u]) continue;
		dfs2(v,v);
	}
}
int lca(int x,int y){
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]]) swap(x,y);
		x=f[top[x]];
	}
	return dep[x]<dep[y]?x:y;
}

int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>m>>s;
	for(int i=1,x,y;i<n;i++){
		cin>>x>>y;
		add_edge(x,y),add_edge(y,x);
	}
	dfs(s,0);
	dfs2(s,s);
//	return 0;
	while(m--){
		int a,b;
		cin>>a>>b;
		cout<<lca(a,b)<<'\n';
	}
}

2)倍增

点击查看代码
int dep[maxn],f[maxn][22];
void dfs(int u,int fa){
	dep[u]=dep[fa]+1;
	for(int i=0;i<=19;i++)
		f[u][i+1]=f[f[u][i]][i];
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(v==fa) continue;
		f[v][0]=u;
		dfs(v,u);
	}
}
int lca(int x,int y){
	if(dep[x]<dep[y]) swap(x,y);
	for(int i=20;i>=0;i--){
		if(dep[f[x][i]]>=dep[y]) x=f[x][i];
		if(x==y) return x; 
	}
	for(int i=20;i>=0;i--){
		if(f[x][i]!=f[y][i]){
			x=f[x][i];
			y=f[y][i];
		}
	}
	return f[x][0];
}
int getdis(int x,int y){
	return dep[x]+dep[y]-2*dep[lca(x,y)];
}

3)x-->y 走k步

点击查看代码
int jump(int x,int y,int k){//x往y走k步到达的节点 
	int Lca=lca(x,y);
	if(getdis(x,y)<k) return 0;
	else if(getdis(x,y)==k) return y;
	
	if(getdis(x,Lca)>=k){
		while(k!=0){
			int t=__lg(k);
			x=f[x][t];
			k-=pow(2,t);
		}
		return x;
	}
	else{
		k-=(getdis(x,Lca));
		k=getdis(y,Lca)-k;
		while(k!=0){
			int t=__lg(k);
			y=f[y][t];
			k-=pow(2,t);
		}
		return y;
	}
}

4)k级祖先

点击查看代码
int getk(int u,int k){
	for(int i=0;k;k>>=1,i++){
		if(k&1) u=f[u][i];
	}
	return u;
}

4.最短路

1)堆优化dij

点击查看代码
bool vis[maxn];
int dis[maxn];
struct node{
	int dis,pos;
	bool operator<(const node &x)const{
		return x.dis<dis;
	}
};
void dij(int x){
	memset(vis,0,sizeof(vis));
	memset(dis,0x7f7f7f7f,sizeof(dis));
	priority_queue<node> q;
	dis[x]=0;
	q.push((node){0,x});
	while(!q.empty()){
		node tmp=q.top();
		q.pop();
		int u=tmp.pos,d=tmp.dis;
		if(vis[u]) continue;
		vis[u]=1;
		for(int i=head[u];i;i=e[i].nxt){
			int v=e[i].to;
			if(dis[v]>dis[u]+e[i].w){
				dis[v]=dis[u]+e[i].w;
				if(!vis[v]) q.push((node){dis[v],v});
			}
		} 
	}
}

2)floyd

点击查看代码
for(int k=1;k<=n;k++){
		for(int i=1;i<=n;i++){
			for(int j=1;j<=n;j++){
				dp[i][j]=min(dp[i][k]+dp[k][j],dp[i][j]);
			}
		}
	}

5.字符串相关

1)hash

点击查看代码
ull p[maxn],h[maxn];

p[0]=1;
for(int i=1;i<maxn;i++){
	p[i]=p[i-1]*base;
}
for(int i=1;i<=n;i++){
	h[i]=h[i-1]*base+(ull)s[i]-'a';
}

//求lr区间的hash值
ull gethash(int l,int r){
	return h[r]-h[l-1]*p[r-l+1];
} 

2)KMP

点击查看代码
#include<bits/stdc++.h>
using namespace std;
string a,b;
int la,lb,nxt[1000005];
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>a>>b;
	la=a.size(),lb=b.size();
	a=" "+a,b=" "+b;
	
	for(int i=2,j=0;i<=lb;i++){
		while(j && b[i]!=b[j+1]) 
			j=nxt[j];
		if(b[j+1]==b[i])
			j++;
		nxt[i]=j;
	}
	
	for(int i=1,j=0;i<=la;i++){
		while(j>0 && b[j+1]!=a[i])
			j=nxt[j];
		if(b[j+1]==a[i]) j++;
		if(j==lb){
			cout<<i-lb+1<<'\n';
			j=nxt[j];
		}
	}
	for(int i=1;i<=lb;i++) cout<<nxt[i]<<" ";
} 

6.线性筛

点击查看代码
vis[1]=1;
for(int i=2;i<=100000;i++){
	if(!vis[i]) p[++cnt]=i;
	for(int j=1;j<=cnt && p[j]*i<=100000;j++){
		vis[p[j]*i]=1;
		if(i%p[j]==0) break;
	}
}

7.离散化

点击查看代码
for(int i=1;i<=n;i++){
	lsh[i]=a[i];
}
sort(lsh+1,lsh+1+n);
m=unique(lsh+1,lsh+1+n)-lsh-1;
for(int i=1;i<=n;i++){
	a[i]=lower_bound(lsh+1,lsh+1+m,a[i])-lsh;
}

8.最小生成树

点击查看代码
#include<bits/stdc++.h>
using namespace std;
int n,m,fa[5005],cnt,ans;
struct node{
    int u,v,w;
}a[200005];
bool cmp(node p,node q){
    return p.w<q.w;
}
int find(int x){
    if(fa[x]==x) return x;
    return fa[x]=find(fa[x]);//并查集的基础上
}
int main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++) fa[i]=i;
    for(int i=1;i<=m;i++){
        cin>>a[i].u>>a[i].v>>a[i].w;
    }
    sort(a+1,a+m+1,cmp);
    for(int i=1;i<=m;i++){
        int t1,t2;
        t1=find(a[i].u);
        t2=find(a[i].v);
        if(t1!=t2){
            fa[t1]=t2;
            cnt++;
            ans+=a[i].w;
        }
        if(cnt==n-1) break;
    }
    if(cnt==n-1) cout<<ans;
    else cout<<"orz";//判负权环
    return 0;
}

9.topo排序

点击查看代码
void toposort(){
    queue<int>q;
    for(int i=1;i<=n;i++) {
        if(in[i]==0){  //应提前预处理此数组
            q.push(i);
            dep[i]=1;
        }
    }
    while(!q.empty()){
        int x=q.front(); 
        q.pop();
        cout<<x<<" ";
        for(int i=head[x];i;i=e[i].nxt) {
            int v=e[i].to;
            dep[v]=dep[x]+1; //记录顺序
            ans=max(ans,dep[v]);
            in[v]--;  //一定要记得入度减
            if(in[v]==0) q.push(v); 
        }
    }
}

10.dfs序 欧拉序

11.tarjan全家桶

1)强连通分量

2)缩点

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=10015;
int n,m,a[maxn],s;
struct edge{
	int to,nxt,fr;
}e[10*maxn],ed[10*maxn];
int head[maxn],edgenum;
void add_edge(int u,int v){
	e[++edgenum].nxt=head[u];
	e[edgenum].fr=u;
	e[edgenum].to=v;
	head[u]=edgenum;
}
int low[maxn],dfn[maxn],tim,vis[maxn],vol[maxn];
stack<int> st;
void tarjan(int x){
	low[x]=dfn[x]=++tim;
	st.push(x);
	vis[x]=1;
	for(int i=head[x];i;i=e[i].nxt){
		int v=e[i].to;
		if(!dfn[v]){
			tarjan(v);
			low[x]=min(low[x],low[v]);
		}
		else if(vis[v]){
			low[x]=min(low[x],dfn[v]);
		}
	}
	if(dfn[x]==low[x]){
		int y;
		while(y=st.top()){
			st.pop();
			vol[y]=x;
			vis[y]=0;
			if(x==y) break;
			a[x]+=a[y];
		}
	}
}
int h[maxn],in[maxn],dis[maxn];
int topo(){
	queue<int> q;
	int tot=0;
	for(int i=1;i<=n;i++){
		if(vol[i]==i && !in[i]){
			q.push(i);
			dis[i]=a[i];
		}
	}
	while(!q.empty()){
		int x=q.front();
		q.pop();
		for(int i=h[x];i;i=ed[i].nxt){
			int v=ed[i].to;
			dis[v]=max(dis[v],dis[x]+a[v]);
			in[v]--;
			if(in[v]==0) q.push(v);
		}
	}
	int ans=0;
	for(int i=1;i<=n;i++) ans=max(ans,dis[i]);
	return ans;
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++) cin>>a[i];
	for(int i=1,u,v;i<=m;i++){
		cin>>u>>v;
		add_edge(u,v);	
	}
	for(int i=1;i<=n;i++){
		if(!dfn[i]) tarjan(i);
	}
	for(int i=1;i<=m;i++){
		int x=vol[e[i].fr],y=vol[e[i].to];
		if(x!=y){
			ed[++s].nxt=h[x];
			ed[s].to=y;
			h[x]=s;
			in[y]++;
		}
	}
	cout<<topo()<<endl;
}

3)边双

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=2e6+10;
int n,m;
struct edge{
	int to,nxt;
}e[2*maxn];
int head[maxn],edgenum=1;
void add_edge(int u,int v){
	e[++edgenum].nxt=head[u];
	e[edgenum].to=v;
	head[u]=edgenum;
}
int dfn[maxn],low[maxn],tim,cnt,col[maxn];
int st[maxn],top;
bool b[2*maxn];//i是否为割边 
int tot[maxn];
vector<int> ans[maxn];
stack<int> s;
void tarjan(int u,int fa){
	dfn[u]=low[u]=++tim;
	s.push(u);
	for(int i=head[u];i;i=e[i].nxt){
		int v=e[i].to;
		if(!dfn[v]){
			tarjan(v,i);
			low[u]=min(low[u],low[v]);
			if(dfn[u]<low[v]){
				b[i]=b[i^1]=1;//该边及其反向边均为桥 
			}
		}
		else if(i!=(fa^1))
			low[u]=min(low[u],dfn[v]);
	}
	if(dfn[u]==low[u]){
		++cnt;
		while(1){
			int t=s.top();
			s.pop();
			col[t]=cnt;
			if(u==t) break;
		}
	}
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>m;
	for(int i=1,u,v;i<=m;i++){
		cin>>u>>v;
		add_edge(u,v);
		add_edge(v,u);
	}
	for(int i=1;i<=n;i++){
		if(!dfn[i]) tarjan(i,0);
	}
	cout<<cnt<<endl;
	for(int i=1;i<=n;i++){
		tot[col[i]]++;
		ans[col[i]].push_back(i);
	}
	int now=1;
	while(tot[now]){
		cout<<tot[now]<<" ";
		for(int i=0;i<ans[now].size();i++)
			cout<<ans[now][i]<<" ";
		now++;
		cout<<endl;
	}
} 

12.dp 相关

其实我只会朴素01背包朴素LIS一点点状压,你说得对但我考场上真能推出来 dp 方程吗

1)线性 dp

通常设 dp[i][j] 表示前i个,以j结尾的xx最大值,如果第一位没用的话可以滚掉,时间复杂度 n^2,如果dp式子没问题考虑优化的话,想单调性,数据结构

2)区间dp

通常设 dp[i][j] 表示i--j这个区间的贡献,然后枚举断点,合并求值。

点击查看代码
for(int len=1;len<=n;len++){
	for(int i=1;i<=n-len+1;i++){
		int j=i+len-1;
		for(int k=i;k<j;k++){
			//状态转移方程
		}
	}
}

然后套路:

如果是枚举断点or合并的话,一般都有dp[i][j]=dp[i][k]+dp[k][j]

然后还可以向两边扩展,比如 dp[i][j]=max/min(dp[i+1][j]+a[i],dp[i][j-1]+a[j],dp[i+1][j-1]+a[i]+a[j])

转移的时候注意分类讨论,是否合法以及是否越界

如果实在难以维护,考虑多开一维or多开一个数组,辅助转移

优化时间复杂度可以考虑前缀和or差分

3)背包dp

这个非常简单嗷,经典背包九讲

01背包
#include<bits/stdc++.h>
using namespace std;
int t,m;
int tim[105],val[105];
int dp[1005];
int ans=-1;
int main(){
	cin>>t>>m;
	for(int i=1;i<=m;i++){
		cin>>tim[i]>>val[i];
	}
	for(int i=1;i<=m;i++){
		for(int j=t;j>=0;j--){
			if(j>=tim[i])
				dp[j]=max(dp[j],dp[j-tim[i]]+val[i]);
		}
	}
	for(int i=1;i<=t;i++){
		ans=max(dp[i],ans); 
	}
	cout<<ans<<endl;
	return 0;
}
完全背包
#include<bits/stdc++.h>
using namespace std;
int n,m;
int v[105],w[105];
int dp[305];
int ans=-1;
int main(){
	cin>>n>>m;
	for(int i=1;i<=m;i++){
		cin>>v[i]>>w[i];
	}
	for(int i=1;i<=m;i++){
		for(int j=v[i];j<=n;j++){//相比于01背包,这个是从小到大枚举的
			dp[j]=max(dp[j],dp[j-v[i]]+w[i]);
		}
	}
	cout<<"max="<<dp[n]<<endl;
	return 0;
}
分组背包
#include<bits/stdc++.h>
using namespace std;
int w[10005],q[10005],p[1000][1000],dp[10005];
int main(){
	int s,v,n,t;
	cin>>v>>n>>t;
	for(int i=1;i<=n;i++){
		cin>>w[i]>>q[i]>>s;
		p[s][0]++;
		p[s][p[s][0]]=i;
	}
	for(int i=1;i<=t;i++){
		for(int j=v;j>=0;j--){
			for(int k=1;k<=p[i][0];k++){
				if(j>=w[p[i][k]])
					dp[j]=max(dp[j],dp[j-w[p[i][k]]]+q[p[i][k]]);
			}
		}
	}
	cout<<dp[v]<<endl;
	return 0;
}

哦哦还有树上背包

点击查看代码
#include<iostream>
#include<cstdio>
#define maxn 200
#define maxm 400
using namespace std;
int n,v,cnt,w[maxn],c[maxn],dp[maxn][maxm];
//dp[u][i]表示从以u为根的子树中选,总体积不超过j的max价值
int root;
int head[maxn],dis[maxn],vis[maxn];
struct node{
    int to,next;
}e[maxm];
// 链式前向星 或者叫 邻接表
//加边操作
void add(int x,int y){
    cnt++;
    e[cnt].to=y;
    e[cnt].next=head[x];
    head[x]=cnt;
}

void dfs(int k){ //当前节点k
    for(int i=head[k];i;i=e[i].next){// 枚举物品
        int son=e[i].to; // 记录子节点
        dfs(son);// 向下递归到最末子树 在回溯的过程中从最末更新dp值 直到回到root
        // 由于当前节点k必选 因此体积j需要将c[k]空出来 01背包倒序枚举体积
        for(int j=v-c[k];j>=0;j--){ 
            for(int l=0;l<=j;l++){// 枚举决策
                dp[k][j]=max(dp[k][j],dp[k][j-l]+dp[son][l]);
            }//             不选son子树    选son子树
        }
    }
    for(int i=v;i>=c[k];i--) dp[k][i]=dp[k][i-c[k]]+w[k];
    for(int i=0;i<c[k];i++) dp[k][i]=0;
}

int main(){
    scanf("%d%d",&n,&v);
    for(int i=1;i<=n;i++){
        int p;
        scanf("%d%d%d",&c[i],&w[i],&p);
        if(p==-1) root=i; // 根节点
        add(p,i); // 加边加边 由父节点指向子节点
    }
    dfs(root); // 从根节点开始搜
    printf("%d",dp[root][v]);
}

4)状压dp

特点是数据范围很小,可以用来骗分(,经常把状态压成二进制位(eg:0/1表示不选or选。dp方程看自己发挥吧。。

5)树形dp

树形dp

13.网络流相关

1.Dinic

点击查看代码
#include<bits/stdc++.h>
#define int long long 
using namespace std;
const int maxn=1e6+10,inf=1e9;
int n,m,s,t;
struct edge{
	int to,nxt,val;
}e[2*maxn];
int head[maxn],dep[maxn],cur[maxn],edgenum=1;
void add(int u,int v,int w){
	e[++edgenum].nxt=head[u];
	e[edgenum].to=v;
	e[edgenum].val=w;
	head[u]=edgenum;
}
void add_edge(int u,int v,int w){
	add(u,v,w),add(v,u,0);
}

bool bfs(){
	memset(dep,0,sizeof(dep));
	queue<int> q;
	q.push(s),dep[s]=1;
	while(!q.empty()){
		int x=q.front();q.pop();
		for(int i=head[x];i;i=e[i].nxt){
			int v=e[i].to;
			if(dep[v] || e[i].val<=0) continue;
			dep[v]=dep[x]+1;
			q.push(v);
		}
	}
	return dep[t]>0;
}

int dfs(int u,int flow){//flow指从u流出的流量 
	if(u==t || !flow) return flow;
	int res=0;
	for(int i=cur[u];i;i=e[i].nxt){
		cur[u]=i;
		int v=e[i].to;
		if(dep[v]==dep[u]+1 && e[i].val>0){
			int f=dfs(v,min(e[i].val,flow));
			e[i].val-=f,e[i^1].val+=f;
			flow-=f,res+=f;
			if(flow==0) break;
		} 
	} 
	return res; 
}
int dinic(){
	int res=0;
	while(bfs()){
		memcpy(cur,head,sizeof(head));
		res+=dfs(s,inf);
	}
	return res;
}

signed main(){
	ios::sync_with_stdio(0);
	cin.tie(),cout.tie(0);
	cin>>n>>m>>s>>t;
	for(int i=1,u,v,w;i<=m;i++){
		cin>>u>>v>>w;
		add_edge(u,v,w);
	}
	cout<<dinic()<<endl;
}

2.费用流

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=2e5+10,inf=2e9;

int n,m,s,t;
struct edge{
	int to,nxt,val,c;
}e[2*maxn];
int head[maxn],edgenum=1;
void add(int u,int v,int w,int c){
	e[++edgenum].nxt=head[u];
	e[edgenum].to=v;
	e[edgenum].val=w;
	e[edgenum].c=c;
	head[u]=edgenum;
}
void add_edge(int u,int v,int w,int c){
	add(u,v,w,c),add(v,u,0,-c);
}

int dis[maxn],pre[maxn],minw[maxn];
//分别表示 费用之和 增广路上每一个点上一条边的编号 剩余流量最小值 
bool vis[maxn];
bool spfa(){
	for(int i=1;i<=n;i++){
		dis[i]=inf,vis[i]=0;
	} 
	queue<int> q;
	q.push(s);
	dis[s]=0,minw[s]=inf,vis[s]=1;
	
	while(!q.empty()){
		int x=q.front();q.pop();
		vis[x]=0;
		for(int i=head[x];i;i=e[i].nxt){
			int v=e[i].to,w=e[i].val;
			if(w>0 && dis[x]+e[i].c<dis[v]){
				dis[v]=dis[x]+e[i].c;
				minw[v]=min(minw[x],w);
				pre[v]=i;
				if(!vis[v]){
					vis[v]=1,q.push(v);
				} 
			}
		}
	}
	return dis[t]<inf;
}

int mf,mc;//最大流量和最小费用
void EK(){
	while(spfa()){
		mf+=minw[t],mc+=minw[t]*dis[t];
		int i;
		for(int x=t;x!=s;x=e[i^1].to){
			i=pre[x];
			e[i].val-=minw[t],e[i^1].val+=minw[t];
		}
	}
} 
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n>>m>>s>>t;
	for(int i=1,u,v,w,c;i<=m;i++){
		cin>>u>>v>>w>>c;
		add_edge(u,v,w,c);
	}
	EK();
	cout<<mf<<" "<<mc<<endl;
}

14.SA

点击查看代码
void SA(){
	m=128;
	for(int i=1;i<=n;i++) x[i]=s[i],cnt[x[i]]++;
	for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1];
	for(int i=n;i>=1;i--) sa[cnt[x[i]]--]=i;
	
	for(int k=1;k<=n;k<<=1){
		int num=0;
		for(int i=n-k+1;i<=n;i++) y[++num]=i;
		for(int i=1;i<=n;i++){
			if(sa[i]>k) y[++num]=sa[i]-k;
		}
		memset(cnt,0,sizeof(cnt));
		for(int i=1;i<=n;i++) cnt[x[i]]++;
		for(int i=1;i<=m;i++) cnt[i]+=cnt[i-1];
		for(int i=n;i>=1;i--) sa[cnt[x[y[i]]]--]=y[i],y[i]=0;
		swap(x,y);
		num=1,x[sa[1]]=1;
		for(int i=2;i<=n;i++){
			if(y[sa[i]]==y[sa[i-1]] && y[sa[i]+k]==y[sa[i-1]+k])
				x[sa[i]]=num;
			else x[sa[i]]=++num;
		}
		if(num==n) break;
		m=num;
	}
}
void height(){
	int k=0;
	for(int i=1;i<=n;i++){
		if(x[i]==0) continue;
		if(k) k--;
		while(s[i+k]==s[sa[x[i]-1]+k]) k++;
		ht[x[i]]=k;
	}
}

15.回文自动机

点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=5e5+10;
string s,t;
int n;

struct PAM{
	int len,fail,son[30];
	int dep;
}pam[maxn];
int getf(int x,int i){//向上跳fail指针 找满足条件的节点 
	while(i-pam[x].len-1<0 || s[i-pam[x].len-1]!=s[i]) 
		x=pam[x].fail;
	return x;
}
int lst,tot=1;
void insert(int i){
	int pos=getf(lst,i),ch=s[i]-'a';
	if(pam[pos].son[ch]==0){//以前没有出现 添加
		pam[++tot].fail=pam[getf(pam[pos].fail,i)].son[ch];
		pam[pos].son[ch]=tot;
		pam[tot].len=pam[pos].len+2;
		pam[tot].dep=pam[pam[tot].fail].dep+1;
	}
	lst=pam[pos].son[ch];
	return;
} 

int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>t;
	n=t.size();
	pam[0].fail=1,pam[1].len=-1;
	int lans=0;
	for(int i=0;i<n;i++){
		char ch=t[i];
		if(lans) ch=char(((int)ch-97+lans)%26+97);
		s+=ch;
		insert(i);
		cout<<pam[lst].dep<<" ";
		lans=pam[lst].dep;
	}
}

16.后缀自动机

点击查看代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=2e6+10,inf=2e9;
int n;
string s;

int tot,lst;
struct SAM{
	int len,link,son[30];
}p[maxn];
void init(){
	p[0].link=p[1].link=-1;
	tot=lst=1;
}

ll siz[maxn],ans;
void insert(int i){
	p[++tot].len=p[lst].len+1;
	int pos=lst,ch=s[i]-'a';
	siz[tot]=1;
	lst=tot;
	while(pos!=-1 && p[pos].son[ch]==0){
		p[pos].son[ch]=tot;
		pos=p[pos].link;
	}
	if(pos==-1) p[tot].link=1;
	else{
		int u=pos,v=p[pos].son[ch];
		if(p[u].len+1==p[v].len) p[tot].link=v;
		else{
			p[++tot]=p[v];
			p[tot].len=p[u].len+1;
			p[v].link=p[tot-1].link=tot;
			while(pos!=-1 && p[pos].son[ch]==v){
				p[pos].son[ch]=tot;
				pos=p[pos].link;
			}
		}
	}
}

vector<int> e[maxn];
void build(){
	init();
	for(int i=1;i<=n;i++) insert(i);
	for(int i=1;i<=tot;i++){
		e[p[i].link].push_back(i);
	}
}
bool vis[maxn];
void dfs(int x){
	for(auto i:e[x]){
		dfs(i);
		siz[x]+=siz[i];
	}
	if(siz[x]>1) ans=max(ans,siz[x]*p[x].len);
}
int main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>s;
	n=s.size();
	s=" "+s;
	build();
	dfs(1);
	cout<<ans<<endl;
}
//空串代表的节点是1

17.其他

改题的时候发现自己不会写线段树上二分,存一个给自己看

树状数组求逆序对!

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int maxn=5e5+10;
int n,a[maxn],d[maxn],ans;

int tr[maxn<<2];
int lowbit(int x){
	return x&(-x);
}
void add(int x,int val){
	for(int i=x;i<=n;i+=lowbit(i)){
		tr[i]+=val;
	}
}
int query(int x){
	int ans=0;
	for(int i=x;i;i-=lowbit(i)){
		ans+=tr[i];
	}
	return ans;
}
signed main(){
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;i++) cin>>a[i],d[i]=a[i];
	sort(a+1,a+1+n);
	int tmp=unique(a+1,a+1+n)-a-1;
	for(int i=1;i<=n;i++){
		d[i]=lower_bound(a+1,a+1+tmp,d[i])-a;
	}
	
	for(int i=n;i>=1;i--){
		add(d[i],1);
		ans+=query(d[i]-1);
	}
	cout<<ans<<endl;
}
posted @ 2025-10-31 11:04  Aapwp  阅读(45)  评论(1)    收藏  举报
我给你一个没有信仰的人的忠诚