1487: [HNOI2009]无归岛

Description


Neverland是个神奇的地方,它由一些岛屿环形排列组成,每个岛上都生活着之中与众不同的物种。但是这些物种都有一个共同的生活习性:对于同一个岛上的任意两个生物,他们有且仅有一个公共朋友,即对同一岛上的任意两个生物a和b有且仅有一个生物c既是a的朋友也是b的朋友,当然某些岛上也可能会只有一个生物孤单地生活着。这一习性有一个明显的好处,当两个生物发生矛盾的时候,他们可以请那个唯一的公共朋友来裁决谁对谁错。

另外,岛与岛之间也有交流,具体来说,每个岛都会挑选出一个最聪明的生物做代表,然后这个生物与他相邻的两个岛的代表成为朋友。

不行的是,A世界准备入侵Neverland,作为Neverland的守护者,Lostmonkey想知道在一种比较坏的情况下Never的战斗力。因为和朋友并肩作战,能力会得到提升,所以Lostmonkey想知道在不选出一对朋友的情况下Neverland的最大战斗力。即选出一些生物,且没有一对生物是朋友,并且要求它们的战斗力之和最大。

Input

第一行包含用空格隔开的两个整数n和m,分别表示Neverland的生物种数和朋友对数。接下来的m行描述所有朋友对,具体来说,每行包含用空格隔开的两个整数a和b,表示生物a和生物b是朋友(每对朋友只出现一次)。第m+2行包含用空格隔开的n个整数,其中第i个整数表示生物i的战斗力Ai。输入数据保证4<=n<=100000,1<=a,b<=n,1<=m<=200000,-1000<=Ai<=1000.

Output

 

仅包含一个整数,表示满足条件的最大战斗力。

Sample Input



6 7
1 2
2 3
3 4
4 1
3 6
3 5
5 6
20 10 30 15 20 10

Sample Output


50
题解:又一道仙人掌,感觉比上一题要简单,
假设没有环的话,就是一道常规DP,f[i]表示选i,g[i]表示不选。
f[i]=∑(g[son]+val[x])
g[i]=∑max(f[son],g[son])
但如果放在环上的话,就有些蛋疼了。
比如rt->x1->x2->rt这个环,我要选rt的话,x1,x2都不能选。
所以,我们DP两遍,第一遍表示不选rt,第二遍表示选。
u1表示可以取,u0不可以取,v1取了后的值,v0没取的值
通过给u1的初始值来枚举选不选x1
则v1=u1+f[x],v0=u0+g[x]
u1=v0,u0=max(v1,v0)
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int N=100008;
struct node{
	int y,next;
}sa[400010];int first[N],len=0;
int n,m;
void ins(int x,int y)
{
	len++;
	sa[len].y=y;
	sa[len].next=first[x];
	first[x]=len;
}
int dfn[N],low[N],dep[N],fa[N],cnt=0,f[N],g[N];
void dp(int root,int x)
{
	int u1=0,u0=0,v1,v0;//u1表示可以取,u0不可以取,v1取了后的值,v0没取的值
	for(int i=x;i!=root;i=fa[i])
	{
		v1=u1+f[i];v0=u0+g[i];
		u1=v0;u0=max(v1,v0);
	 } 
	 //因为x取了,所以root不能取 
	 g[root]+=u0;
	 u1=-99999999,u0=0;
	for(int i=x;i!=root;i=fa[i])
	{
		v1=u1+f[i];v0=u0+g[i];
		u1=v0;u0=max(v1,v0);
	 } 
	 //因为u1太小,所以x没有取 
	 f[root]+=u1;
}int val[N];
void dfs(int x)
{
	cnt++;dfn[x]=low[x]=cnt;
	for(int i=first[x];i!=-1;i=sa[i].next)
	{
		int y=sa[i].y;
		if(y!=fa[x])
		{
			if(!dfn[y])
			{
				dep[y]=dep[x]+1;
				fa[y]=x;
				dfs(y);
				low[x]=min(low[x],low[y]);
			}
			else low[x]=min(low[x],dfn[y]);
		}
	}
	f[x]=val[x];
	for(int i=first[x];i!=-1;i=sa[i].next)
	{
		int y=sa[i].y;
		if(fa[y]!=x&&dfn[x]<dfn[y])
		dp(x,y);
	}
}

int main()
{
	memset(first,-1,sizeof(first));
	scanf("%d%d",&n,&m);
	for(int i=1;i<=m;i++)
	{
		int a,b;
		scanf("%d%d",&a,&b);
		ins(a,b);ins(b,a);
	}
	for(int i=1;i<=n;i++)
	scanf("%d",&val[i]);
	dfs(1);
	printf("%d",max(f[1],g[1])); 
 } 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值