LOI 2607 [ZJOI2008]骑士

##题目描述##
Z国的骑士团是一个很有势力的组织,帮会中汇聚了来自各地的精英。他们劫富济贫,惩恶扬善,受到社会各界的赞扬。
最近发生了一件可怕的事情,邪恶的Y国发动了一场针对Z国的侵略战争。战火绵延五百里,在和平环境中安逸了数百年的Z国又怎能抵挡的住Y国的军队。于是人们把所有的希望都寄托在了骑士团的身上,就像期待有一个真龙天子的降生,带领正义打败邪恶。
骑士团是肯定具有打败邪恶势力的能力的,但是骑士们互相之间往往有一些矛盾。每个骑士都有且仅有一个自己最厌恶的骑士(当然不是他自己),他是绝对不会与自己最厌恶的人一同出征的。
战火绵延,人民生灵涂炭,组织起一个骑士军团加入战斗刻不容缓!国王交给了你一个艰巨的任务,从所有的骑士中选出一个骑士军团,使得军团内没有矛盾的两人(不存在一个骑士与他最痛恨的人一同被选入骑士军团的情况),并且,使得这支骑士军团最具有战斗力。
为了描述战斗力,我们将骑士按照1至N编号,给每名骑士一个战斗力的估计,一个军团的战斗力为所有骑士的战斗力总和。
##输入输出##
输入格式:
输入文件knight.in第一行包含一个正整数N,描述骑士团的人数。
接下来N行,每行两个正整数,按顺序描述每一名骑士的战斗力和他最痛恨的骑士。
输出格式:
输出文件knight.out应包含一行,包含一个整数,表示你所选出的骑士军团的战斗力。
##数据范围##
对于30%的测试数据,满足N ≤ 10;
对于60%的测试数据,满足N ≤ 100;
对于80%的测试数据,满足N ≤ 10 000。
对于100%的测试数据,满足N ≤ 1 000 000,每名骑士的战斗力都是不大于 1 000 000的正整数。
##分析##
虽然题目中所给的条件是环,但是可以破环转换成没有上司的舞会的一类的树形dp问题,还是比较简单的,主要注意破环后的两个点不能同时去的情况。

#include <bits/stdc++.h>
#define rep( i , l , r ) for( int i = (l) ; i <= (r) ; ++i )
#define per( i , r , l ) for( int i = (r) ; i >= (l) ; --i )
#define erep( i , u ) for( int i = head[(u)] ; ~i ; i = e[i].nxt )
using namespace std;
int _read(){
	char ch = getchar();
	int x = 0 , f = 1 ;
	while( !isdigit( ch ) )
		   if( ch == '-' ) f = -1 , ch = getchar();
		   else ch = getchar();
	while( isdigit( ch ) )
		   x = (ch  - '0') + (x << 3) + (x << 1) , ch =  getchar();
	return x * f;
}
const int maxn = 1000000 + 5;
int head[maxn] , atk[maxn] , _t = 0;
struct edge{
	int v , nxt;
} e[maxn * 2];
inline void addedge( int u , int v ){
	e[_t].v = v , e[_t].nxt = head[u] , head[u] = _t++;
}
typedef long long ll;
const ll INF = 0x7f7f7f7f7f7f;
ll f[maxn][2];
bool vis[maxn] , flg = 0;
int cir1 , cir2;
void dfs( int u , int fa ){ // Find circle
	vis[u] = 1;
	erep( i , u ){
		int v = e[i].v;
		if( v == fa ) continue;
		if( !vis[v] ) dfs( v , u );
		else if( vis[v] && !flg ){ flg = 1; cir1 = u , cir2 = v; e[i].v = e[i ^ 1].v = 0; } 
	}
}
void DP( int u , int fa ){
	f[u][0] = 0;
	f[u][1] = atk[u];
	erep( i , u ){
		int v = e[i].v;
		if( !v || v == fa ) continue;
		DP( v , u );
		f[u][1] += f[v][0];
		f[u][0] += max( f[v][1] , f[v][0] );
	}
}
int main(){
	memset( head , 0xff , sizeof head );
	int N = _read() , t;
	rep( i , 1 , N ){
	    atk[i] = _read() , t = _read();
		addedge( i , t ) ;  addedge( t , i );
	}
	ll ans = 0;
	rep( i , 1 , N ){
		if( vis[i] ) continue;
		flg = 0 , cir1 = cir2 = 0;
		dfs( i , -1 );
		if( !flg ){ DP( i , -1 ); ans += max( f[i][0] , f[i][1] ); }
		else{
			ll res = -INF;
			DP( cir1 , -1 );
			res = max( res , f[cir1][0] );
			DP( cir2 , -1 );
			res = max( res , f[cir2][0] );
			ans += res;
		}
	}	
	cout << ans << endl;
	return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值