高级数据结构手册

LCA

//exam:P3379 【模板】最近公共祖先(LCA)
#include <iostream>
#include <cstdio>
#include <vector>
#define int long long 
using namespace std;
const int MAXN=5e5+5,MAXM=25;
void dfs(int,int);
int lca(int,int);
vector<int> g[MAXN];
int deep[MAXN];
int n,q,s;
signed main(){
	cin>>n>>q>>s;
	for(int i=1,u,v;i<n;i++){
		cin>>u>>v;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	dfs(s,0);
	while(q--){
		int a,b;
		cin>>a>>b;
		cout<<lca(a,b)<<endl;
	}
	return 0;
} 
int f[MAXN][MAXM];
void dfs(int u,int fa){
	f[u][0]=fa,deep[u]=deep[fa]+1;
	for(int i=1;i<=20;i++){
		f[u][i]=f[f[u][i-1]][i-1];
	}
	for(int i=0;i<g[u].size();i++){
		int v=g[u][i];
		if(v!=fa){
			dfs(v,u);
		}
	}
}
int lca(int u,int v){
	if(deep[u]<deep[v]){
		swap(u,v);
	}
	int l=deep[u]-deep[v];
	for(int i=0;i<=20;i++){
		if(l&(1<<i)){
			u=f[u][i];
		} 
	}
	if(u==v){
		return u;
	}
	for(int i=20;i>=0;i--){
		if(f[u][i]!=f[v][i]){
			u=f[u][i],v=f[v][i];
		}
	}
	return f[u][0];
}

树状数组

//exam:T588519 树状数组 3 :区间修改,区间查询
#include <iostream>
#include <cstdio>
#define int long long 
#define lowbit(x) ((x)&(-(x)))
using namespace std;
const int MAXN=1e6+5;
int query(int x);
void update(int x,int v);
int tree1[MAXN],tree2[MAXN];
int n,q;
signed main(){
	ios::sync_with_stdio(false);
	cin>>n>>q;
	for(int i=1,u;i<=n;i++){
		cin>>u;
		update(i,u);
		update(i+1,-u);
	}
	while(q--){
		int op,l,r,x;
		cin>>op>>l>>r;
		if(op==1){
			cin>>x;
			update(l,x);
			update(r+1,-x);
		}else{
			cout<<query(r)-query(l-1)<<endl;
		}
	}
	return 0;
} 
int query(int x){
	int res=0,res1=0,t=x;
	while(x){
		res+=tree1[x];
		res1+=tree2[x];
		x-=lowbit(x);
	}
	return res*(t+1)-res1;
}
void update(int x,int v){
	int v1=x*v;
	while(x<=n){
		tree1[x]+=v;
		tree2[x]+=v1;
		x+=lowbit(x);
	}
}

线段树

加法

//exam:P3372 【模板】线段树 1
#include <iostream>
#include <cstdio>
#define int long long 
using namespace std;
const int MAXN=1e5+5;
void build(int,int,int);
int query(int,int,int);
void update(int,int,int,int);
int a[MAXN];
int n,m;
signed main(){
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	build(1,1,n);
	for(int i=1,op,x,y,k;i<=m;i++){
		cin>>op>>x>>y;
		if(op==1){
			cin>>k;
			update(1,x,y,k);
		} else{
			cout<<query(1,x,y)<<endl;
		}
//		for(int i=1;i<=n;i++){
//			cout<<query(1,i,i)<<" ";
//		}
//		cout<<endl;
	} 
	return 0;
}
struct SegmentTree{
	int value,l,r,add;
	#define value(x) tree[x].value//节点权值 
	#define l(x) tree[x].l
	#define r(x) tree[x].r
	#define add(x) tree[x].add
	#define ltree(x) ((x)<<1)
	#define rtree(x) (((x)<<1)+1)
	#define treesize(x) (r(x)-l(x)+1)
	#define mid(x) ((l(x)+r(x))>>1)
}tree[MAXN*4];
void build(int root,int l,int r){
	l(root)=l,r(root)=r;
	if(l==r){
		value(root)=a[l];
		return ;
	}
	int mid=mid(root);
	build(ltree(root),l,mid);
	build(rtree(root),mid+1,r);
	value(root)=value(ltree(root))+value(rtree(root));
	return ;
}
void spread(int x){
	value(ltree(x))+=add(x)*treesize(ltree(x));
	value(rtree(x))+=add(x)*treesize(rtree(x));
	add(ltree(x))+=add(x);
	add(rtree(x))+=add(x);
	add(x)=0;
}
int query(int root,int l,int r){
	if(l<=l(root)&&r(root)<=r){
		return value(root);
	}
	spread(root);
	int mid=mid(root),res=0;
	if(mid>=l){
		res+=query(ltree(root),l,r);
	}if(mid<r){
		res+=query(rtree(root),l,r);
	}
	return res;
}
void update(int root,int l,int r,int x){
	if(l<=l(root)&&r(root)<=r){
		add(root)+=x;
		value(root)+=x*treesize(root);
		return ;
	}
	spread(root);
	int mid=mid(root);
	if(mid>=l){
		update(ltree(root),l,r,x);
	}if(mid<r){
		update(rtree(root),l,r,x);
	}
	value(root)=value(ltree(root))+value(rtree(root));
}

加法和乘法

//exam:P3373 【模板】线段树 2
#include <iostream>
#include <cstdio>
#define int long long 
using namespace std;
const int MAXN=1e5+5;
void build(int,int,int);
int query(int,int,int);
void update1(int,int,int,int);
void update2(int,int,int,int);
int a[MAXN];
int n,m,P;
signed main(){
	ios::sync_with_stdio(false);
	cin>>n>>m>>P;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	build(1,1,n);
	for(int i=1,op,x,y,k;i<=m;i++){
		cin>>op>>x>>y;
		if(op==1){
			cin>>k;
			update2(1,x,y,k);
		} else if(op==2){
			cin>>k;
			update1(1,x,y,k);
		} else{
			cout<<query(1,x,y)<<endl;
		}
//		cout<<"#";
//		for(int i=1;i<=n;i++){
//			cout<<query(1,i,i)<<" ";
//		}
//		cout<<endl;
	} 
	return 0;
}
struct SegmentTree{
	int value,l,r,add,mul;
	#define value(x) tree[x].value//节点权值 
	#define l(x) tree[x].l
	#define r(x) tree[x].r
	#define add(x) tree[x].add
	#define mul(x) tree[x].mul
	#define ltree(x) ((x)<<1)
	#define rtree(x) (((x)<<1)+1)
	#define treesize(x) (r(x)-l(x)+1)
	#define mid(x) ((l(x)+r(x))>>1)
}tree[MAXN*4];
void build(int root,int l,int r){
	l(root)=l,r(root)=r,mul(root)=1;
	if(l==r){
		value(root)=a[l];
		return ;
	}
	int mid=mid(root);
	build(ltree(root),l,mid);
	build(rtree(root),mid+1,r);
	value(root)=value(ltree(root))+value(rtree(root));
	return ;
}
void spread(int x){
	value(ltree(x))=value(ltree(x))*mul(x)%P; 
	value(rtree(x))=value(rtree(x))*mul(x)%P; 
	value(ltree(x))=(value(ltree(x))+add(x)*treesize(ltree(x))%P)%P;
	value(rtree(x))=(value(rtree(x))+add(x)*treesize(rtree(x))%P)%P;
	add(ltree(x))=(add(ltree(x))*mul(x)%P+add(x))%P;
	add(rtree(x))=(add(rtree(x))*mul(x)%P+add(x))%P;
	mul(ltree(x))=mul(ltree(x))*mul(x)%P;
	mul(rtree(x))=mul(rtree(x))*mul(x)%P;
	add(x)=0,mul(x)=1;
}
int query(int root,int l,int r){
	if(l<=l(root)&&r(root)<=r){
		return value(root);
	}
	spread(root);
	int mid=mid(root),res=0;
	if(mid>=l){
		res=(res+query(ltree(root),l,r))%P;
	}if(mid<r){
		res=(res+query(rtree(root),l,r))%P;
	}
	return res;
}
void update1(int root,int l,int r,int x){
	if(l<=l(root)&&r(root)<=r){
		add(root)=(add(root)+x)%P;
		value(root)=(value(root)+x*treesize(root)%P)%P;
		return ;
	}
	spread(root);
	int mid=mid(root);
	if(mid>=l){
		update1(ltree(root),l,r,x);
	}if(mid<r){
		update1(rtree(root),l,r,x);
	}
	value(root)=(value(ltree(root))+value(rtree(root)))%P;
}
void update2(int root,int l,int r,int x){
	if(l<=l(root)&&r(root)<=r){
		add(root)=add(root)*x%P;
		mul(root)=mul(root)*x%P;
		value(root)=(value(root)*x%P)%P;
		return ;
	}
	spread(root);
	int mid=mid(root);
	if(mid>=l){
		update2(ltree(root),l,r,x);
	}if(mid<r){
		update2(rtree(root),l,r,x);
	}
	value(root)=(value(ltree(root))+value(rtree(root)))%P;
}

分块

//exam:243. 分块 
#include <algorithm>
#include <iostream>
#include <cstdio> 
#include <cmath>
#define int long long 
using namespace std;
const int INF=0x3f3f3f3f;
const int MAXN=5e5+5;
void init();
int query(int,int,int);
void update(int,int,int);
pair<int,int> a[MAXN];
int n,m;
int T;
signed main(){
	ios::sync_with_stdio(false);
	cin>>n;
	for(int i=1;i<=n;i++){
		int t;
		cin>>t;
		a[i]=make_pair(t,i);
	}
	init();
	for(int i=1;i<=n;i++){
		int op,l,r,x;
		cin>>op>>l>>r>>x;
		if(op==1){
			cout<<query(l,r,x*x)<<endl;
		}else{
			update(l,r,x);
		}
	}
	return 0;
}
int pos[MAXN],L[MAXN],R[MAXN],add[MAXN];
void init(){
	T=sqrt(n);
	for(int i=1;i<=T;i++){
		L[i]=(i-1)*T+1,R[i]=i*T;
	}
	if(R[T]<n){
		L[T+1]=R[T]+1,R[T+1]=n;
		T++;
	}
	for(int i=1;i<=T;i++){
		sort(a+L[i],a+1+R[i]);
		for(int j=L[i];j<=R[i];j++){
			pos[j]=i;
		}
	}
}
int query(int x,int y,int k){
	int l=pos[x],r=pos[y],ans=0;
	if(l==r){
		for(int i=L[l];i<=R[l];i++){
			if(a[i].second>=x&&a[i].second<=y&&a[i].first<k-add[l]){
				ans++;
			}
		}
	}else{
		for(int i=L[l];i<=R[l];i++){
			if(a[i].second>=x&&a[i].first<k-add[l]){
				ans++;
			}
		}
		for(int i=l+1;i<=r-1;i++){
			ans+=lower_bound(a+L[i],a+R[i]+1,make_pair(k-add[i],-INF))-a-L[i];
		}
		for(int i=L[r];i<=R[r];i++){
			if(a[i].second<=y&&a[i].first<k-add[r]){
				ans++;
			}
		}
	}
	return ans;
}
void update(int x,int y,int k){
	int l=pos[x],r=pos[y];
	if(l==r){
		for(int i=L[l];i<=R[l];i++){
			if(a[i].second>=x&&a[i].second<=y){
				a[i].first+=k;
			}
		}
		sort(a+L[l],a+R[l]+1);
	}else{
		for(int i=L[l];i<=R[l];i++){
			if(a[i].second>=x){
				a[i].first+=k;
			}
		}
		sort(a+L[l],a+R[l]+1);
		for(int i=l+1;i<=r-1;i++){
			add[i]+=k;
		}
		for(int i=L[r];i<=R[r];i++){
			if(a[i].second<=y){
				a[i].first+=k;
			}
		}
		sort(a+L[r],a+R[r]+1);
	}
}

A*算法

//exam:P2901 [USACO08MAR] Cow Jogging G
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <queue>
#define int long long 
using namespace std;
struct node{
	int next,w;
	bool operator < (const node &nxt) const{
		if(w!=nxt.w){
			return w>nxt.w;
		}
		return next>nxt.next;
	}
};
const int MAXN=1005;
void build();//建立估算数组 
void output();
vector<node> g[MAXN],g0[MAXN];
priority_queue<node> q;
int d[MAXN];//估算数组 
int n,m,k;
signed main(){
	ios::sync_with_stdio(false);
	cin>>n>>m>>k;
	for(int i=1,u,v,w;i<=m;i++){
		cin>>u>>v>>w;
		g[u].push_back((node){v,w});
		g0[v].push_back((node){u,w});
	}
	build();
	q.push((node){n,d[n]});
	while(!q.empty()){
		node u=q.top();q.pop();
//		cout<<u.next<<" "<<u.w-d[u.next]<<" "<<u.w<<endl;
		if(u.next==1){
			cout<<u.w<<endl;
			k--;
			if(k==0){
				break;
			}
		}
		for(int i=0;i<g[u.next].size();i++){
			node v=g[u.next][i];
			q.push((node){v.next,d[v.next]+u.w-d[u.next]+v.w});
		}
	}
	while(k){
		cout<<-1<<endl;
		k--;
	}
	return 0;
}
void add(int v,int w){
	if(d[v]>=w){
		d[v]=w;
		q.push((node){v,w});
	}
}
void build(){
	for(int i=1;i<=n;i++){
		d[i]=0x3f3f3f3f;
	}
	add(1,0);
	while(!q.empty()){
		node u=q.top();q.pop();
		if(d[u.next]<u.w){
			continue;
		}
		for(int i=0;i<g0[u.next].size();i++){
			node v=g0[u.next][i];
			add(v.next,u.w+v.w);
		}
	}
}

最小生成树

//exam:P3366 【模板】最小生成树
#include <iostream>
#include <cstdio>
#include <vector>
#include <queue>
#define int long long 
using namespace std;
struct node{
	int v,w;
};
struct edge{
	int u,v,w;
	bool operator < (const edge &nxt) const{
		return w>nxt.w;
	}
};
const int MAXN=5e3+5;
int solve();
bool check();
priority_queue<edge> q;
vector<node> g[MAXN],a[MAXN];
int n,m,ans;
signed main(){
	ios::sync_with_stdio(false);
	cin>>n>>m;
	for(int i=1,u,v,w;i<=m;i++){
		cin>>u>>v>>w;
		g[u].push_back((node){v,w});
		g[v].push_back((node){u,w});
		q.push((edge){u,v,w});
	}
	ans=solve();
	if(!check()){
		cout<<"orz";
	}else{
		cout<<ans;
	}
	return 0; 
} 
bool book[MAXN];
void dfs(int cur){
	book[cur]=true;
	for(int i=0;i<a[cur].size();i++){
		node u=a[cur][i];
		if(!book[u.v]){
			dfs(u.v);
		}
	}
}
bool check(){
	dfs(1);
	for(int i=1;i<=n;i++){
		if(!book[i]){
			return false;
		}
	}
	return true;
}
int fa[MAXN];
void init(){
	for(int i=1;i<=n;i++){
		fa[i]=i;
	} 
}
int find(int x){
	if(fa[x]==x){
		return x; 
	} 
	return fa[x]=find(fa[x]);
}
void unity(int x,int y){
	int a=find(x),b=find(y);
	if(a==b){
		return ;
	} 
	fa[a]=b;
} 
bool same(int x,int y){
	return find(x)==find(y);
}
int solve(){
	init();
	int ans=0;
	while(!q.empty()){
		edge u=q.top();q.pop();
		if(same(u.u,u.v)){
			continue;
		}
		unity(u.u,u.v);
		a[u.u].push_back((node){u.v,u.w});
		a[u.v].push_back((node){u.u,u.w});
		ans+=u.w;
	}
	return ans;
}

埃氏筛

//exam:P3383 【模板】线性筛素数
#include <iostream>
#include <cstdio>
#include <cmath>
//#define int long long
using namespace std;
const int MAXN=2e8+5;
bool book[MAXN];
int a[MAXN],b[MAXN];
int n,q,m;
signed main(){
	ios::sync_with_stdio(false);
	cin>>n>>q;
	for(int i=2;i<=n;i++){
		if(!book[i]){
			b[++m]=i;
			for(int j=i*2;j<=n;j+=i){
				book[j]=true;
			}
		}
	}
	for(int i=1;i<=q;i++){
		int x;
		cin>>x;
		cout<<b[x]<<endl;
	}
	return 0;
} 

线性筛

//exam:P3383 【模板】线性筛素数(线性筛)
#include <iostream>
#include <cstdio>
#include <cmath>
//#define int long long
using namespace std;
const int MAXN=2e8+5;
bool book[MAXN];
int a[MAXN],b[MAXN];
int n,q,m;
signed main(){
	ios::sync_with_stdio(false);
	cin>>n>>q;
	for(int i=2;i<=n;i++){
		if(!book[i]){
			b[++m]=i;
		}
		for(int j=1;j<=m&&i*b[j]<=n;j++){
			book[i*b[j]]=true;
			if(i%b[j]==0){
				break;
			}
		}
	}
	for(int i=1;i<=q;i++){
		int x;
		cin>>x;
		cout<<b[x]<<endl;
	}
	return 0;
} 

扩展欧几里得定理

//exam:P1082 [NOIP 2012 提高组] 同余方程
#include <iostream>
#include <cstdio>
#define int long long 
using namespace std;
void exgcd(int a,int b,int &x,int &y);
int a,b,x,y;
signed main(){
	ios::sync_with_stdio(false);
	cin>>a>>b;
	exgcd(a,b,x,y);
	cout<<x<<" "<<y;
	return 0;
}
void exgcd(int a,int b,int &x,int &y){
	if(b==0){
		x=1,y=0;
		return ;
	}
	exgcd(b,a%b,x,y);
	int x1=x,y1=y;
	x=y1,y=(x1-a/b*y1);
}

费马小定理&错排计数

//exam:P4071 [SDOI2016] 排列计数
#include <iostream>
#include <cstdio>
#define int long long 
#define endl '\n'
using namespace std;
const int P=1e9+7;
const int MAXN=1e6+5;
void build();
int solve(int,int);
int t,n,m;
signed main(){
	ios::sync_with_stdio(false);
	build();
	cin>>t;
	while(t--){
		cin>>n>>m;
		cout<<solve(n,m)<<endl;
	}
	return 0;
}
int f[MAXN],finv[MAXN],dp[MAXN];
int qpow(int x,int y){
	int res=1;
	while(y){
		if((y&1)==1){
			res=res*x%P;
		}
		y>>=1;
		x=x*x%P;
	}
	return res;
}
void build(){
	f[0]=finv[0]=1,dp[2]=1,dp[0]=1;
	for(int i=1;i<=1e6;i++){
		finv[i]=finv[i-1]*qpow(i,P-2)%P;//阶乘逆元
		f[i]=f[i-1]*i%P;
		if(i>=3){
			dp[i]=(i-1)*(dp[i-1]+dp[i-2])%P;
		}
	}
}
int C(int n,int m){
	return f[n]*finv[m]%P*finv[n-m]%P;
}
int solve(int n,int m){
	return C(n,m)*dp[n-m]%P;
}

字符串哈希

//exam:P10468 兔子与兔子
#include <iostream>
#include <cstdio>
#define int unsigned long long
using namespace std;
const int MAXN=1e6+5;
const int P=1331;
int hs(int,int);
string s;
int H[MAXN],PS[MAXN];
int n,x1,x2,y1,y2;
signed main(){
	ios::sync_with_stdio(false);
	cin>>s>>n;s=" "+s;
	PS[0]=1;
	for(int i=1;i<s.size();i++){
		H[i]=H[i-1]*P+s[i];
		PS[i]=PS[i-1]*P;
	}
	while(n--){
		cin>>x1>>y1>>x2>>y2;
		int h1=hs(x1,y1),h2=hs(x2,y2);
		if(h1==h2){
			cout<<"Yes\n";
		}else{
			cout<<"No\n";
		}
	}
	return 0;
}
int hs(int l,int r){
	return H[r]-H[l-1]*PS[r-l+1];
}

Trie

//exam:P10470 前缀统计
#include <iostream>
#include <cstring>
#include <cstdio>
#define int long long
#define endl '\n' 
using namespace std;
const int MAXN=1e6+5;
const int MAXM=30;
struct trie{
	#define idx(c) ((c)-'a')
	int ch[MAXN][MAXM];//ch[i][j]表示节点,j表示边
	int val[MAXN];//记录当前节点的信息 
	int size;//节点数量 
	trie(){//构造函数 
		size=0;
		memset(ch[0],0,sizeof(ch[0]));
	}
	void insert(string s){
		int u=0;
		for(int i=0;i<s.size();i++){
			int t=idx(s[i]);
			if(ch[u][t]==0){
				size++;
				memset(ch[size],0,sizeof(ch[size]));
				ch[u][t]=size;
			}
			u=ch[u][t];
		}
		val[u]++;
	}
	int query(string s){
		int u=0,res=val[0];
		for(int i=0;i<s.size();i++){
			int t=idx(s[i]);
			if(ch[u][t]==0){
				break;
			}
			u=ch[u][t];
			res+=val[u];
		}
		return res;
	}
};
int n,m;
trie t;
signed main(){
	ios::sync_with_stdio(false);
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		string s;
		cin>>s;
		t.insert(s);
	}
	for(int i=1;i<=m;i++){
		string s;
		cin>>s;
		cout<<t.query(s)<<endl;
	}
	return 0;
}

KMP

//exam:P3375 【模板】KMP
#include <iostream>
#include <cstdio>
#define int long long 
using namespace std;
const int MAXN=1e6+5;
void getF(string);
int fail[MAXN];//失配 
string s1,s2;
int cur;
signed main(){
	ios::sync_with_stdio(false);
	cin>>s2>>s1;//模式串,匹配串 
	getF(s1);
	for(int i=0;i<s2.size();i++){
		if(s1[cur]==s2[i]){
			cur++;
		}else{
			while(cur&&s2[i]!=s1[cur]){
				cur=fail[cur];
			}
			cur=(s2[i]==s1[cur]?(cur+1):0);
		}
		if(cur==s1.size()){
			cout<<i-s1.size()+2<<endl; 
			cur=fail[cur];
		}
	}
	for(int i=1;i<=s1.size();i++){
		cout<<fail[i]<<" ";
	}
	return 0;
}
void getF(string s){
	fail[0]=fail[1]=0;
	int l=s.size();
	for(int i=1;i<l;i++){//从i状态算i+1 
		int cur=fail[i];//失配之后到达的状态
		while(cur&&s[cur]!=s[i]){//不断失配 
			cur=fail[cur];
		} 
		fail[i+1]=(s[cur]==s[i]?(cur+1):0);
	}
}

Tarjan

割点

//exam name: P3388 【模板】割点(割顶)
#include <iostream>
#include <cstdio>
#include <vector>
#define int long long 
using namespace std;
const int MAXN=2e4+5;
bool check(int);
void tarjan(int,int);
vector<int> g[MAXN];
bool book[MAXN];
int n,m,res;
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
	cin>>n>>m;
	for(int i=1,u,v;i<=m;i++){
		cin>>u>>v;
		g[u].push_back(v);
		g[v].push_back(u);
	}
	for(int i=1;i<=n;i++){
		if(!check(i)){
			tarjan(i,i);
		}
	}
	for(int i=1;i<=n;i++){
		res+=book[i];
	}
	cout<<res<<endl;
	for(int i=1;i<=n;i++){
		if(book[i]){
			cout<<i<<" ";
		}
	}
	return 0;
} 
int dfn[MAXN],low[MAXN];
int dfncnt;
bool check(int u){
	return dfn[u];
}
void tarjan(int u,int root){
	low[u]=dfn[u]=++dfncnt;
	int size=0;
	for(auto v:g[u]){
		if(!dfn[v]){
			tarjan(v,root);
			low[u]=min(low[u],low[v]);
			if(low[v]>=dfn[u]&&u!=root){
				book[u]=true;
			}
			if(u==root){
				size++;
			}
		}else{
			low[u]=min(low[u],dfn[v]);
		}
	} 
	if(size>=2&&u==root){
		book[u]=true;
	}
}

缩点

#include <iostream>
#include <cstdio>
#include <vector>
#include <stack>
#include <queue>
#define int long long 
using namespace std;
const int MAXN=1e5+5;
bool check(int);
void tarjan(int);
void topsort();
vector<int> g[MAXN],G[MAXN];
int indeed[MAXN],belong[MAXN];
int a[MAXN],u[MAXN],v[MAXN];
int f[MAXN];
int n,m,ans;
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0);cout.tie(0);
	cin>>n>>m;
	for(int i=1;i<=n;i++){
		cin>>a[i];
	}
	for(int i=1;i<=m;i++){
		cin>>u[i]>>v[i];
		g[u[i]].push_back(v[i]);
	}
	for(int i=1;i<=n;i++){
		if(!check(i)){
			tarjan(i);
		}
	}
	for(int i=1;i<=m;i++){
		int x=belong[u[i]],y=belong[v[i]];
		if(x!=y){// 顶点不同
			G[x].push_back(y);
			indeed[y]++;
		}
	}
	topsort();
	for(int i=1;i<=n;i++){
		ans=max(ans,f[i]);
	}
	cout<<ans;
	return 0;
}
stack<int> s;
bool book[MAXN];
int low[MAXN],dfn[MAXN];
int scc[MAXN];// 节点在强连通分量中的编号
int len[MAXN];// 这个强连通分量的大小 
int dfncnt,size;
bool check(int u){
	return dfn[u];
} 
void tarjan(int u){
	low[u]=dfn[u]=++dfncnt;// 赋 dfs 序编号 
	s.push(u);
	book[u]=true;// 在栈中
	for(auto v:g[u]){
		if(!dfn[v]){// 这个点没有遍历过 
			tarjan(v);
			low[u]=min(low[u],low[v]);
		}else if(book[v]){// 在栈中
			low[u]=min(low[u],dfn[v]); 
		}
	}
	if(dfn[u]==low[u]){// 强连通分量的起始点 
		++size;
		while(true){
			int t=s.top();s.pop();
			scc[t]=size,book[t]=false;
			belong[t]=u;// 链接点 
			len[size]++;
			if(t==u){
				break;
			}
			a[u]+=a[t];// 缩点 
		}
	}
}
queue<int> q;
void topsort(){// 拓扑排序
	for(int i=1;i<=n;i++){
		if(i==belong[i]&&!indeed[i]){
			q.push(i);
			f[i]=a[i];
		}
	}
	while(!q.empty()){
		int u=q.front();q.pop();
		for(auto v:G[u]){
			f[v]=max(f[v],f[u]+a[v]);
			indeed[v]--;
			if(!indeed[v]){
				q.push(v);
			} 
		}
	}
}

马拉车算法

//exam: P3805 【模板】Manacher
#include <iostream>
#include <cstdio>
using namespace std;
const int MAXN = 2.2e7+5;
void input();
int solve();
signed main() {
	ios::sync_with_stdio(false);
	cin.tie(0), cout.tie(0);
	input();
	cout << solve();
	return 0;
}
char s[MAXN];
int f[MAXN];
int size;
void input() { // 读入处理好后的字符串 
	char c = getchar();
	size = 1;
	s[0] = '~', s[1] = '|';
	while(c < 'a' || c > 'z') { // 跳过无用字符 
		c = getchar(); 
	}
	while('a' <= c && c <= 'z') {
		s[++size] = c;
		s[++size] = '|'; // 加入分隔符 
		c = getchar();
	}
}
int solve() { // 马拉车算法
	int r = 0, mid = 0;
	int res = 0;
	for(int t = 1; t <= size; t++) {
		if(t <= r) {
			f[t] = min(f[(mid << 1) - t], r - t + 1);
		}
		while(s[t - f[t]] == s[t + f[t]]) { // 暴力拓展左右两侧 
			f[t]++;
		}
		if(f[t] + t > r) {
			r = f[t] + t - 1;
			mid = t;
		}
		res = max(res, f[t]);
	} 
	return res - 1;
}
posted @ 2025-09-20 08:16  戊日·兰园  阅读(41)  评论(0)    收藏  举报
风雨随风亦随浪,梦境环空本是空。如若将要打破时,心存一气自心中。