CF1389G题解

首先对于一个点双内的点,一定可以将其边定向成一个强联通分量。所以可以将这个图缩点,问题变到了树上。

将这 \(k\) 个点在树上标记出来,我们需要选取一颗原图的子树满足叶子结点必须是这 \(k\) 个点中的点。(差不多就是建个虚树然后把父子边在原图上对应的边拉出来)

定义每个点的权值是自身与其连接的非选取树节点的点权之和,这个时候一个连通块的大小被定义为点权值和减去边权之和,问题变为包含某个点的连通块的权值最大是多少。

可以发现父亲方向和儿子方向无关,于是考虑换根 DP。这个非常简单不再阐述了。

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

好麻烦啊,要建3次图

#include<cstdio>
#include<cctype>
typedef long long ll;
const int M=3e5+5;
int n,m,k,ege,t[M],sz[M],bl1[M],bl2[M],f[M],h[M],u[M],v[M],w[M];
int BCC,dfc,low[M],dfn[M];int tp,stk[M];bool itk[M];
ll w1[M],w2[M],F[M],G[M],ans[M];
struct Edge{
	int v,w,nx;
}e[M<<1];
inline void Add(const int&u,const int&v,const int&w){
	e[++ege]=(Edge){v,w,h[u]};h[u]=ege;
	e[++ege]=(Edge){u,w,h[v]};h[v]=ege;
}
inline int min(const int&a,const int&b){
	return a>b?b:a;
}
inline ll max(const ll&a,const ll&b){
	return a>b?a:b;
}
inline void Tarjan(const int&u,const int&fa){
	low[u]=dfn[u]=++dfc;itk[stk[++tp]=u]=true;
	for(int v,E=h[u];E;E=e[E].nx)if((v=e[E].v)^fa){
		if(!dfn[v])Tarjan(v,u),low[u]=min(low[u],low[v]);
		else if(itk[v])low[u]=min(low[u],dfn[v]);
	}
	if(low[u]==dfn[u]){
		int v;++BCC;do itk[v=stk[tp--]]=false,w2[bl1[v]=BCC]+=w1[v];while(u^v);
	}
}
inline bool LCA(const int&u,const int&fa,int&lca){
	if(sz[u])return lca=u,true;int cnt(0);
	for(int v,E=h[u];E;E=e[E].nx)if((v=e[E].v)^fa){
		if(LCA(v,u,lca))if(++cnt==2)return lca=u,true;
	}
	return false;
}
inline void init(const int&u){
	for(int v,E=h[u];E;E=e[E].nx)if((v=e[E].v)^f[u])f[v]=u,init(v),sz[u]+=sz[v];
	if(!sz[u])w2[f[u]]+=w2[u],bl2[u]=f[u];else bl2[u]=u;
}
inline int Find(const int&u){
	return bl2[u]==u?u:bl2[u]=Find(bl2[u]);
}
inline void DP1(const int&u){
	for(int v,E=h[u];E;E=e[E].nx)if(v=e[E].v,v^f[u]&&sz[v])DP1(v),F[u]+=max(0,F[v]-e[E].w);F[u]+=w2[u];
}
inline void DP2(const int&u){
	ll S(w2[u]);for(int v,E=h[u];E;E=e[E].nx)if(v=e[E].v,sz[v])S+=max(0,v==f[u]?G[u]:F[v]-e[E].w);
	for(int v,E=h[u];E;E=e[E].nx)if(v=e[E].v,v^f[u]&&sz[v])G[v]=max(0,S-max(0,F[v]-e[E].w)-e[E].w),DP2(v);
}
inline int read(){
	int n(0);char s;while(!isdigit(s=getchar()));while(n=n*10+(s&15),isdigit(s=getchar()));return n;
}
inline void write(ll n){
	static char s[20];int top(0);while(s[++top]=n%10^48,n/=10);while(putchar(s[top]),--top);putchar(32);
}
signed main(){
	int lca;n=read();m=read();k=read();for(int i=1;i<=k;++i)t[i]=read();for(int i=1;i<=n;++i)w1[i]=read();
	for(int i=1;i<=m;++i)w[i]=read();for(int i=1;i<=m;++i)u[i]=read(),v[i]=read(),Add(u[i],v[i],w[i]);
	Tarjan(1,0);for(int i=1;i<=n;++i)h[i]=0;ege=0;for(int i=1;i<=k;++i)++sz[bl1[t[i]]];
	for(int i=1;i<=m;++i)if(bl1[u[i]]^bl1[v[i]])Add(bl1[u[i]],bl1[v[i]],w[i]);
	LCA(1,0,lca);sz[f[lca]]-=k;init(lca);DP1(lca);DP2(lca);for(int i=1;i<=BCC;++i)ans[i]=F[i]+G[i];
	for(int i=1;i<=n;++i)write(ans[Find(bl1[i])]);
}
posted @ 2022-08-18 08:10  Prean  阅读(18)  评论(0编辑  收藏  举报
var canShowAdsense=function(){return !!0};