CodeForces - 1220E Tourism(边双缩点+树形dp)

题目链接:点击查看

题目大意:给出一个由n个点和m条边构成的无向图,每个点都有一个权值,现在给出起点st,问从起点出发,如何规划路线可以使得途径的权值最大,唯一的约束是一条边不能连续经过,比如当前从u->v,下一次不可以从v->u

题目分析:首先要理解好题目,题目中的一条边不能连续经过两次,不代表只能经过两次,换句话说,若几个点可以构成环,那么他们就可以在环上跑一圈然后再出去,所以我们可以先缩点,因为是无向图,我们选择边双缩点,只要缩点后的集合中的点大于一个点的话,那么当前集合肯定能构成环,缩点后整个图变成了一棵树,我们就可以直接设计dp了,因为如果一个点有环的话是可以多次经过的,那么我们不妨可以将所有新点内大于1的集合压缩至其父亲,以至于让dp写起来更简单,完成压缩后自底向上维护一条链上的最大值就好了

代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
using namespace std;
    
typedef long long LL;
    
const int inf=0x3f3f3f3f;

const int N=2e5+100;
 
const int M=4e5+100;
 
struct Egde
{
	int to,next;
}edge1[M],edge2[M];
 
int head1[N],head2[N],low[N],dfn[N],c[N],num,cnt1,cnt2,dcc,n,m,sz[N];

LL w[N],val[N],dp[N],mmax;
 
bool bridge[M];
 
void addedge1(int u,int v)
{
	edge1[cnt1].to=v;
	edge1[cnt1].next=head1[u];
	head1[u]=cnt1++;
}
 
void addedge2(int u,int v)
{
	edge2[cnt2].to=v;
	edge2[cnt2].next=head2[u];
	head2[u]=cnt2++;
}
 
void tarjan(int u,int in_edge)
{
	dfn[u]=low[u]=++num;
	for(int i=head1[u];i!=-1;i=edge1[i].next)
	{
		int v=edge1[i].to;
		if(!dfn[v])
		{
			tarjan(v,i);
			low[u]=min(low[u],low[v]);
			if(low[v]>dfn[u])
				bridge[i]=bridge[i^1]=true;
		}
		else if(i!=(in_edge^1))
			low[u]=min(low[u],dfn[v]);
	}
}
 
void dfs(int u)
{
	c[u]=dcc;
	sz[dcc]++;
	val[dcc]+=w[u];
	for(int i=head1[u];i!=-1;i=edge1[i].next)
	{
		int v=edge1[i].to;
		if(c[v]||bridge[i])
			continue;
		dfs(v);
	}
}
 
void solve()
{
	for(int i=1;i<=n;i++)//找桥 
		if(!dfn[i])
			tarjan(i,0);
	for(int i=1;i<=n;i++)//缩点 
		if(!c[i])
		{
			dcc++;
			dfs(i);
		}
}

void dfs1(int u,int fa)
{
	for(int i=head2[u];i!=-1;i=edge2[i].next)
	{
		int v=edge2[i].to;
		if(v==fa)
			continue;
		dfs1(v,u);
		if(sz[v]>1)
		{
			sz[u]+=sz[v];
			val[u]+=val[v];
			val[v]=0;
		} 
	}
}

void dfs2(int u,int fa)
{
	dp[u]=val[u];
	for(int i=head2[u];i!=-1;i=edge2[i].next)
	{
		int v=edge2[i].to;
		if(v==fa)
			continue;
		dfs2(v,u);
		dp[u]=max(dp[u],dp[v]+val[u]);
	}
}
 
void build()//缩点+连边 
{
	solve();
	for(int i=2;i

 

你可能感兴趣的:(树形dp,图论)