图论·强连通分量

强连通分量

一、Tarjan

关于拓扑序:\(tarjan\)后拓扑序是倒序,所以直接倒序循环就相当于跑拓扑序了。原理待补

复杂度 \(O(n+m)\)

板子代码:

void dfs(int x)
{
	dfn[x]=low[x]=++tme;//时间戳
	st.push(x);
	int _size=e[x].size();
	for (int i=0;i<_size;i++)
	{
		int v=e[x][i];
		if (!dfn[v]) dfs(v),low[x]=min(low[x],low[v]);
		else if (!col[v]) low[x]=min(low[x],dfn[v]);
	}
	
	if (dfn[x]==low[x])
	{
		vis[x]=++cnt;
		sum[cnt]+=c[x];
		while (st.top()!=x)
		{
			vis[st.top()]=cnt;
			sum[cnt]+=c[st.top()];
			st.pop();
		}
		st.pop();		
	}
}
void tarjan()
{
	for (int i=1;i<=n;i++)
	{
		if (!dfn[i]) dfs(i);
	}
}
void build()//缩点建图
{
	for (int i=1;i<=n;i++)
	{
		int _size=e[i].size();
		for (int j=0;j<_size;j++)
		{
			int v=e[i][j];
			if (col[i]!=col[v]) e2[col[i]].push_back(col[v]);
		}
	}
}

【YbtOj】例题

A.有向图缩点

板子题,\(Tarjan\)缩点后拓扑序跑\(dp\)统计即可

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e4+5;
int n,m;
vector <int> e[N];
vector <int> e2[N];
int ind[N];
int c[N];
int ans;

int dfn[N],low[N];
int vis[N];
stack <int> st;
int tme;
int sum[N],cnt;
void dfs(int x)
{
	
	dfn[x]=low[x]=++tme;
	st.push(x);
	int _size=e[x].size();
	for (int i=0;i<_size;i++)
	{
		int v=e[x][i];
		if (!dfn[v])
		{
			dfs(v);
			low[x]=min(low[x],low[v]);
		}
		else if (!vis[v]) low[x]=min(low[x],low[v]);
	}
	
	if (dfn[x]==low[x])
	{
		vis[x]=++cnt;
		sum[cnt]+=c[x];
		while (st.top()!=x)
		{
			vis[st.top()]=cnt;
			sum[cnt]+=c[st.top()];
			st.pop();
		}
		st.pop();		
	}
}
void build()
{
	for (int i=1;i<=n;i++)
	{
		int _size=e[i].size();
		for (int j=0;j<_size;j++)
		{
			int v=e[i][j];
			if (vis[i]!=vis[v]) e2[vis[i]].push_back(vis[v]),ind[vis[v]]++;
		}
	}
}
queue <int> q;
int f[N];
signed main()
{
	scanf("%lld%lld",&n,&m);
	for (int i=1;i<=n;i++) scanf("%lld",&c[i]);
	for (int i=1,u,v;i<=m;i++)
	{
		scanf("%lld%lld",&u,&v);
		e[u].push_back(v);
	}
	
	for (int i=1;i<=n;i++)
	{
		if (dfn[i]==0) dfs(i);
	}
	
	build();
	
	for (int i=1;i<=cnt;i++)
	{
		if (ind[i]==0) q.push(i),f[i]=sum[i];
	}
	while (!q.empty())
	{
		int u=q.front();
		q.pop();
		int _size=e2[u].size();
		for (int i=0;i<_size;i++)
		{
			int v=e2[u][i];
			f[v]=max(f[v],f[u]+sum[v]);
			ind[v]--;
			if (!ind[v]) q.push(v);
		}
	}
	for (int i=1;i<=cnt;i++) ans=max(ans,f[i]);
	printf("%lld",ans);
	return 0;
}

B.受欢迎的牛

可以想到,一个强联通分量里的牛都是互相喜欢的(滑稽脸。当缩完点后,可以发现所有奶牛块的喜欢都会有传递性的,最后那个被传递到的奶牛块就是明星奶牛块,也就是出度为0的奶牛块是明星奶牛块。但要是有多个出度为 \(0\) 的奶牛块,那么就不存在明星奶牛块了。

#incIude <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e4+5;
int n,m;
vector <int> e[N];
vector <int> e2[N];
int f[N];
int tol;
int ans;

int dfn[N],low[N],col[N],cnt,tme;
int sum[N],ind[N];
stack <int> s;
void tarjan(int x)
{
	s.push(x);
	dfn[x]=low[x]=++tme;
	int _size=e[x].size();
	for (int i=0;i<_size;i++)
	{
		int v=e[x][i];
		if (!dfn[v]) tarjan(v),low[x]=min(low[x],low[v]);
		else if (!col[v]) low[x]=min(low[x],low[v]);
	}
	
	if (dfn[x]==low[x])
	{
		col[x]=++cnt;
		sum[cnt]++;
		while (s.top()!=x)
		{
			sum[cnt]++;
			col[s.top()]=cnt;
			s.pop();
		}
		s.pop();
	}
}
void build()
{
	for (int i=1;i<=n;i++)
	{
		int _size=e[i].size();
		for (int j=0;j<_size;j++)
		{
			int v=e[i][j];
			if (col[i]!=col[v]) e2[col[i]].push_back(col[v]),ind[col[i]]++;
		}
	}
}
void topSort()
{
	queue <int> q;
	for (int i=1;i<=cnt;i++)
	{
		if (!ind[i]) q.push(i);
		f[i]=sum[i];
	}
	while (!q.empty())
	{
		int u=q.front();
		q.pop();
		int _size=e2[u].size();
		for (int i=0;i<_size;i++)
		{
			int v=e2[u][i];
			f[v]+=f[u];
			ind[v]--;
			if (!ind[v]) q.push(v);
		}
	}
}
signed main()
{
	scanf("%lld%lld",&n,&m);
	for (int i=1,a,b;i<=m;i++)
	{
		scanf("%lld%lld",&a,&b);
		e[a].push_back(b);
	}
	
	for (int i=1;i<=n;i++)
	{
		if (!dfn[i]) tarjan(i); 
	}
	
	build();
	for (int i=1;i<=cnt;i++)
	{
		if (!ind[i]) ans+=sum[i],tol++;
		if (tol>1) {  printf("0");return 0;  }
	}
	printf("%lld",ans);
	return 0;
}

C.最大半连通子图

史一般的题目叙述翻译题意后,发现所谓半连通就是一条链上串着几个环,要求的就是最长链的长度以及个数。所以缩点之后跑最长链即可,注意重边。

#incIude <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5;
int n,m,x;
vector <int> e[N];
vector <int> e2[N];
int g[N],f[N];
int ans1,ans2;
int klee[N];

int dfn[N],low[N],col[N],tme;
int sum[N],cnt;
int vis[N];
stack <int> st;
void tarjan(int x)
{
	st.push(x);
	dfn[x]=low[x]=++tme;
	int _size=e[x].size();
	for (int i=0;i<_size;i++)
	{
		int v=e[x][i];
		if (!dfn[v]) tarjan(v),low[x]=min(low[x],low[v]);
		else if (!col[v]) low[x]=min(low[x],dfn[v]);
	}
	
	if (dfn[x]==low[x])
	{
		col[x]=++cnt;
		sum[cnt]++;
		while (st.top()!=x)
		{
			col[st.top()]=cnt;
			sum[cnt]++;
			st.pop();
		}
		st.pop();
	}
}
void build()
{
	for (int i=1;i<=n;i++)
	{
		g[i]=1;
		f[i]=sum[i];
		int _size=e[i].size();
		for (int j=0;j<_size;j++)
		{
			int v=e[i][j];
			if (col[i]!=col[v]) e2[col[i]].push_back(col[v]);
		}
	}
}
int used[N];
void topSort()
{
	for (int i=cnt;i>=1;i--)
	{
		int _size=e2[i].size();
		for (int j=0;j<_size;j++)
		{
			int v=e2[i][j];
			if (used[v]==i) continue;
			used[v]=i;
			if (f[v]<f[i]+sum[v])
			{
				f[v]=f[i]+sum[v];
				g[v]=g[i];
			}
			else if (f[v]==f[i]+sum[v]) g[v]=(g[v]+g[i])%x;
			//ans1=max(ans1,f[v]);
		}
	}
}
signed main()
{
	scanf("%lld%lld%lld",&n,&m,&x);
	for (int i=1,a,b;i<=m;i++)
	{
		scanf("%lld%lld",&a,&b);
		e[a].push_back(b);
	}
	
	for (int i=1;i<=n;i++)
	{
		if (!dfn[i]) tarjan(i);
	}
	build();
	topSort();
	for (int i=1;i<=cnt;i++)
	{
		if (ans1<f[i]) 
		{
			ans1=f[i];
			ans2=g[i];
		}
		else if (f[i]==ans1) ans2=(ans2+g[i])%x;
	}
	
	printf("%lld\n%lld",ans1,ans2);
	return 0;
}

D.恒星的亮度

放个差分约束小链接

对于题中给出的所有关系,都可以简化为 \(a_{i}+b\leqslant a_{j}\),所以就可以从 \(i\)\(j\) 连一条权值为 \(b\) 的边。因为一个强联通分量中的所有点权应该相同,若其中存在点权为 \(1\) 的点,那么一定无解。

缩完点后,跑一遍拓扑序更新并记录点权和即可。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e5+5;
int n,m;
struct node {  int nxt,val;  };
vector <node> e[N];
vector <node> e2[N];
int c[N],num[N];
int ind[N];
int ans;

int dfn[N],low[N],tme,col[N],cnt;
stack <int> st;
void tarjan(int x)
{
	dfn[x]=low[x]=++tme;
	st.push(x);
	int _size=e[x].size();
	for (int i=0;i<_size;i++)
	{
		int v=e[x][i].nxt;
		if (!dfn[v]) tarjan(v),low[x]=min(low[x],low[v]);
		else if (!col[v]) low[x]=min(low[x],dfn[v]);
	}
	
	if (dfn[x]==low[x])
	{
		col[x]=++cnt;
		num[cnt]++;
		while (st.top()!=x)
		{
			col[st.top()]=cnt;
			num[cnt]++;
			st.pop();
		}
		st.pop(),c[cnt]=1;
	}
}
void topS()
{
	queue <int> q;
	for (int i=1;i<=cnt;i++) 
	{
		if (ind[i]==0) q.push(i);
	}
	
	while (!q.empty())
	{
		int t=q.front();
		q.pop();
		ans+=c[t]*num[t];
		
		int _size=e2[t].size();
		for (int i=0;i<_size;i++)
		{
			int v=e2[t][i].nxt;
			c[v]=max(c[v],c[t]+e2[t][i].val);
			ind[v]--;
			if (!ind[v]) q.push(v);
		}
	}
}
signed main()
{
	scanf("%lld%lld",&n,&m);
	for (int i=1,t,a,b;i<=m;i++)
	{
		scanf("%lld%lld%lld",&t,&a,&b);
		switch (t)
		{
			case 1:e[a].push_back({b,0});e[b].push_back({a,0});break;
			case 2:e[a].push_back({b,1});break;
			case 3:e[b].push_back({a,0});break;
			case 4:e[b].push_back({a,1});break;
			case 5:e[a].push_back({b,0});break;
		}
	} 
	
	for (int i=1;i<=n;i++) if (!dfn[i]) tarjan(i);

	for (int i=1;i<=n;i++)
	{
		int _size=e[i].size();
		for (int j=0;j<_size;j++)
		{
			int v=e[i][j].nxt;
			if (col[i]!=col[v]) e2[col[i]].push_back({col[v],e[i][j].val}),ind[col[v]]++;
			else if (e[i][j].val==1) {  printf("-1"); return 0;  }
		}
	}
	
	topS() , printf("%lld",ans);
	return 0;
}

E.网络传输

缩点,缩点后强连通分量中的权值就归零了,跑一遍最短路即可。

#incIude <bits/stdc++.h>
#define int long long
#define pii pair<int,int>
using namespace std;
const int N=2e5+5;
int n,m;
struct node{
	int nxt,val;
};
vector <node> e[N];
vector <node> e2[N];

int dfn[N],low[N],tme,sum[N],cnt,col[N];
stack <int> s;
void tarjan(int x)
{
	s.push(x);
	dfn[x]=low[x]=++tme;
	int _size=e[x].size();
	for (int i=0;i<_size;i++)
	{
		int v=e[x][i].nxt;
		if (!dfn[v]) tarjan(v),low[x]=min(low[x],low[v]);
		else if (!col[v]) low[x]=min(low[x],dfn[v]);
	}
	
	if (dfn[x]==low[x])
	{
		col[x]=++cnt;
		while (s.top()!=x)
		{
			col[s.top()]=cnt;
			s.pop();
		}
		s.pop();
	}
}
priority_queue < pii,vector <pii>,greater<pii> > q;
int dis[N];
int vis[N];
int dijkstra()
{
	memset(dis,0x3f,sizeof dis);
	dis[col[1]]=0;
	q.push(make_pair(0,col[1]));
	while (!q.empty())
	{
		int u=q.top().second;
		q.pop();
		if (vis[u]) continue;
		vis[u]=1;
		int _size=e2[u].size();
		for (int i=0;i<_size;i++)
		{
			int v=e2[u][i].nxt,w=e2[u][i].val;
			if (dis[v]>dis[u]+w)
			{
				dis[v]=dis[u]+w;
				q.push(make_pair(dis[v],v));
			}
		}
	}
	return dis[col[n]];
}
signed main()
{
	scanf("%lld%lld",&n,&m);
	for (int i=1,u,v,w;i<=m;i++)
	{
		scanf("%lld%lld%lld",&u,&v,&w);
		e[u].push_back((node){v,w});
	}
	
	for (int i=1;i<=n;i++)
	{
		if (!dfn[i]) tarjan(i);
	}
	for (int i=1;i<=n;i++)
	{
		int _size=e[i].size();
		for (int j=0;j<_size;j++)
		{
			int v=e[i][j].nxt,w=e[i][j].val;
			if (col[i]!=col[v]) e2[col[i]].push_back((node){col[v],w});
		}
	}
	
	printf("%lld",dijkstra());
	return 0;
}

F.通讯问题

先缩点,缩完点后贪心构造方案(是没法跑MST的):对于点\(u\),一定由它相连边权最小的边转移而来,于是都不用建图,处理出所有点转移的最小边权相加就是答案。

#incIude <bits/stdc++.h>
#define int long long
using namespace std;
const int N=5e4+5;
int n,m;
struct node{
	int nxt,val;
};
vector <node> e[N];
vector <node> e2[N];
int sum[N];
int ans;

int dfn[N],low[N],cnt,tme,num[N],col[N];
stack <int> st;
void init()
{
	memset(dfn,0,sizeof dfn);
	memset(low,0,sizeof low);
	memset(num,0,sizeof num);
	memset(col,0,sizeof col);
	memset(sum,0x3f,sizeof sum);
	cnt=tme=ans=0;
	for (int i=0;i<N;i++) e[i].clear(),e2[i].clear();
}
void tarjan(int x)
{
	dfn[x]=low[x]=++tme;
	st.push(x);
	int _size=e[x].size();
	for (int i=0;i<_size;i++)
	{
		int v=e[x][i].nxt;
		if (!dfn[v]) tarjan(v),low[x]=min(low[x],low[v]);
		else if (!col[v]) low[x]=min(low[x],dfn[v]);
	}
	
	if (dfn[x]==low[x])
	{
		col[x]=++cnt;
		num[cnt]++;
		while (st.top()!=x)
		{
			col[st.top()]=cnt;
			num[cnt]++;
			st.pop();
		}
		st.pop();
	} 
}
signed main()
{
	while (scanf("%lld%lld",&n,&m))
	{
		if (n==0&&m==0) break;
		init();
		
		for (int i=1,x,y,c;i<=m;i++)
		{
			scanf("%lld%lld%lld",&x,&y,&c);
			e[x].push_back({y,c});
		}
		
		for (int i=0;i<n;i++)
		{
			if (!dfn[i]) tarjan(i);
		}
		for (int i=0;i<n;i++)
		{
			int _size=e[i].size();
			for (int j=0;j<_size;j++)
			{
				int v=e[i][j].nxt;
				if (col[i]!=col[v]) sum[col[v]]=min(sum[col[v]],e[i][j].val);
			}
		}
		
		sum[col[0]]=0;
		for (int i=1;i<=cnt;i++) ans+=sum[i];
		printf("%lld\n",ans);
	}
	return 0;
}

G.删点次数

在同一个scc内的点不能同时取,在同一条链上的点也不能同时取。所以,在缩点之后跑一遍最长链,最长链上的节点数量就是答案。

#incIude <bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+5;
int n,m;
vector <int> e1[N];
vector <int> e2[N];

stack <int> st;
int dfn[N],low[N],tme,col[N],cnt,num[N];
int ind[N],f[N];
int ans;

void tarjan(int x)
{
	dfn[x]=low[x]=++tme;
	st.push(x);
	int _size=e1[x].size();
	for (int i=0;i<_size;i++)
	{
		int v=e1[x][i];
		if (!dfn[v]) tarjan(v),low[x]=min(low[x],low[v]);
		else if (!col[v]) low[x]=min(low[x],dfn[v]); 
	}
	
	if (dfn[x]==low[x])
	{
		col[x]=++cnt;
		while (st.top()!=x)
		{
			col[st.top()]=cnt,num[cnt]++;
			st.pop();
		}
		f[cnt]=++num[cnt],st.pop();
	}
}
void topS()
{
	queue <int> q;
	for (int i=1;i<=n;i++) if (!ind[i]) q.push(i);
	
	while (!q.empty())
	{
		int t=q.front();
		q.pop();
		ans=max(ans,f[t]);
		
		int _size=e2[t].size();
		for (int i=0;i<_size;i++)
		{
			int v=e2[t][i];
			f[v]=max(f[v],f[t]+num[v]);
			ind[v]--;
			if (!ind[v]) q.push(v);
		}
	}
}
signed main()
{
	scanf("%lld%lld",&n,&m);
	for (int i=1,u,v;i<=m;i++)
	{
		scanf("%lld%lld",&u,&v);
		e1[u].push_back(v);
	}
	
	for (int i=1;i<=n;i++) if (!dfn[i]) tarjan(i);
	for (int i=1;i<=n;i++)
	{
		int _size=e1[i].size();
		for (int j=0;j<_size;j++)
		{
			int v=e1[i][j];
			if (col[i]!=col[v]) e2[col[i]].push_back(col[v]),ind[col[v]]++;
		}
	}
	
	topS(),printf("%lld",ans);
	return 0;
}

H.软件安装

显然可以把依赖关系转化为一个图。处于一个强联通分量的所有点要么全选,要么不选。这样,再看一下数据范围,缩完点后跑一遍树形dp即可。

#incIude <bits/stdc++.h>
#define int long long
using namespace std;
const int N=510;
int n,m;
int w[N],v[N],d[N];
struct node{
	int nxt,val;
};
vector <int> e1[N];
vector <int> e2[N];
int cnt,_w[N],_v[N],ind[N];
int f[N][N];

int dfn[N],low[N],col[N],tme;
stack <int> st;
void tarjan(int x)
{
	st.push(x);
	dfn[x]=low[x]=++tme;
	
	int _size=e1[x].size();
	for (int i=0;i<_size;i++)
	{
		int v=e1[x][i];
		if (!dfn[v]) tarjan(v),low[x]=min(low[x],low[v]);
		else if (!col[v]) low[x]=min(low[x],dfn[v]);
	}
	
	if (dfn[x]==low[x])
	{
		col[x]=++cnt;
		_w[cnt]=w[x],_v[cnt]=v[x];
		while (st.top()!=x)
		{
			col[st.top()]=cnt;
			_w[cnt]+=w[st.top()],_v[cnt]+=v[st.top()];
			st.pop();
		}
		st.pop();
	}
}
void dfs(int x)
{
	int _size=e2[x].size();
	for (int i=0;i<_size;i++)
	{
		int v=e2[x][i];
		dfs(v);
		for (int j=m;j>=_w[x];j--)
		{
			for (int k=_w[v];k<=j-_w[x];k++) f[x][j]=max(f[x][j],f[v][k]+f[x][j-k]);
		}
	}
}
signed main()
{
	scanf("%lld%lld",&n,&m);
	for (int i=1;i<=n;i++) scanf("%lld",&w[i]);
	for (int i=1;i<=n;i++) scanf("%lld",&v[i]);
	for (int i=1;i<=n;i++) 
	{
		scanf("%lld",&d[i]);
		e1[d[i]].push_back(i);
	}
	
	for (int i=1;i<=n;i++) if (!dfn[i]) tarjan(i);
	for (int i=1;i<=n;i++)
	{
		int _size=e1[i].size();
		for (int j=0;j<_size;j++)
		{
			int v=e1[i][j];
			if (col[i]!=col[v])
			{
				e2[col[i]].push_back(col[v]);
				ind[col[v]]++;
			}
		}
	}
	
	for (int i=1;i<=cnt;i++) if (!ind[i]) e2[0].push_back(i);
	for (int i=1;i<=cnt;i++) f[i][_w[i]]=_v[i];
	dfs(0);
	
	printf("%lld",f[0][m]);
	return 0;
}

I.宫室宝藏

显然要建图然后跑 \(tarjan\) 缩点然后 \(dp\),但是这样的话时间复杂度和空间复杂度可能会很爆炸,于是乎我们对于每一行、每一列都建一个虚点,虚点与该行/该列内的宝藏连通,这样就非常好了

我们令 \(x\in [1,r]\) 表示行的虚点,\(y\in [r+1,r+c]\) 表示列的虚点,\(k\in [r+c+1,r+c+n]\) 表示具体的点,每次操作分别连边建图跑 \(tarjan\)即可。

亲测vector爆炸

#incIude <bits/stdc++.h>
#define pii pair<int,int>
using namespace std;
const int N=1e7+5;
int n,r,c;
int cnt;
struct NODE{
	int to,nxt;
}e1[N],e2[N];
map <pii,int> mp;
struct node { int x,y,id; }a[N];
int dx[8]={-1,-1,-1,0,0,1,1,1},dy[8]={-1,0,1,-1,1,-1,0,1};
int sum[N],f[N],ans;

int cnt1,cnt2,head[N],head1[N];
void add(int u,int v) { e1[++cnt1]={v,head[u]}; head[u]=cnt1; }
void add2(int u,int v) { e2[++cnt2]={v,head1[u]}; head1[u]=cnt2; }

int dfn[N],col[N],low[N],tme;
stack <int> st;
void tarjan(int x)
{
	dfn[x]=low[x]=++tme;
	st.push(x);
	for (int i=head[x];i;i=e1[i].nxt)
	{
		int v=e1[i].to;
		if (!dfn[v]) tarjan(v),low[x]=min(low[x],low[v]);
		else if (!col[v]) low[x]=min(low[x],dfn[v]); 
	}
	
	if (dfn[x]==low[x])
	{
		col[x]=++cnt;
		while (st.top()!=x)
		{
			col[st.top()]=cnt,sum[cnt]+=(st.top()>r+c);
			st.pop();
		}
		sum[cnt]+=(st.top()>r+c),st.pop();
	}
}
int main()
{
	scanf("%d%d%d",&n,&r,&c);
	for (int i=1,x,y,t;i<=n;i++)
	{
		scanf("%d%d%d",&x,&y,&t);
		num[i]=1;
		mp[make_pair(x,y)]=i;
		
		add(x,i+r+c),add(y+r,i+r+c);
		if (t==1) add(i+r+c,x);
		else if(t==2) add(i+r+c,y+r);
		else a[++cnt]={x,y,i};
	}
	for (int i=1;i<=cnt;i++)
	{
		for (int j=0;j<8;j++)
		{
			int xx=a[i].x+dx[j],yy=a[i].y+dy[j];
			if (xx>=1&&xx<=r&&yy>=1&&yy<=c&&mp[make_pair(xx,yy)]) 
				add(a[i].id+r+c,mp[make_pair(xx,yy)]+r+c);
		}
	}
	
	cnt=0;
	for (int i=1;i<=n+r+c;i++) if (!dfn[i]) tarjan(i);
	
	for (int i=1;i<=n+r+c;i++)
	{
		for (int j=head[i];j;j=e1[j].nxt)
		{
			int v=e1[j].to;
			if (col[i]!=col[v]) add2(col[i],col[v]);
		}
	}
	for (int i=1;i<=cnt;i++) f[i]=sum[i];
	for (int i=cnt;i>=1;i--)
	{
		for (int j=head1[i];j;j=e2[j].nxt)
		{
			int v=e2[j].to;
			f[v]=max(f[v],f[i]+sum[v]);
		}
	}
	for (int i=1;i<=cnt;i++) ans=max(ans,f[i]);
	printf("%d",ans);
	return 0;
}
posted @ 2024-11-18 23:25  还是沄沄沄  阅读(7)  评论(2编辑  收藏  举报