BZOJ1124: [POI2008]枪战Maf

Description

有n个人,每个人手里有一把手枪。一开始所有人都选定一个人瞄准(有可能瞄准自己)。然后他们按某个顺序开枪,且任意时刻只有一个人开枪。因此,对于不同的开枪顺序,最后死的人也不同。

Input

输入n人数<1000000 每个人的aim

Output

你要求最后死亡数目的最小和最大可能

Sample Input

8
2 3 2 2 6 7 8 5

Sample Output

3 5

HINT

Source

这题不难 稍微推一推就能推出来
基环内向森林
考虑最多:
度数为0的点连锁反应可以打到它能到的所有的点
剩下一堆环, 每个环最少1人存活
答案是N-度数为0的点-环数(自环除外)
考虑最少:
找出度数为0的点,他开枪肯定更优 他的目标的目标如果此时变成度数为0的点 则他的目标的目标进队
剩下一堆环 答案-size环/2
感觉挺难写的
#include <bits/stdc++.h>

using namespace std;

const int maxn = 1000010;

int vis[maxn], d[maxn], to[maxn], n, ans1, ans2, q[maxn], ql, qr;

inline int read()
{
	int tmp = 0; char ch = getchar();
	while( ch < '0' || ch > '9' ) ch = getchar();
	while( ch >= '0' && ch <= '9' ) tmp = tmp * 10 + ch - '0', ch = getchar();
	return tmp;
}

inline void cal_min()
{
	ans1 = n;
	for( int i = 1 ; i <= n ; i++ ) d[ to[ i ] ]++;
	for( int i = 1 ; i <= n ; i++ ) if( to[ i ] == i ) d[ to[ i ] ]--, vis[ i ] = 1;
	for( int i = 1 ; i <= n ; i++ ) if( !d[ i ] && !vis[ i ] ) q[ ++qr ] = i;
	while( ql ^ qr )
	{
		int x = q[ ++ql ];
		vis[ x ] = 1; ans1--;
		if( !vis[ to[ x ] ] )
		{
			vis[ to[ x ] ] = 1;
			if( !vis[ to[ to[ x ] ] ] && !--d[ to[ to[ x ] ] ] ) q[ ++qr ] = to[ to[ x ] ];
		}
	}
	for( int i = 1 ; i <= n ; i++ )
		if( !vis[ i ] )
		{
			int size = 0;
			for( int j = i ; !vis[ j ] ; j = to[ j ] )
				vis[ j ] = 1, size++;
			ans1 -= ( size >> 1 );
		}
}

inline void dfs(int x) { if( vis[ x ] ) return ; vis[ x ] = 1; dfs( to[ x ] ); }

inline void cal_max()
{
	memset( vis, 0, sizeof( vis ) );
	memset( d, 0, sizeof( d ) );
	ans2 = n;
	for( int i = 1 ; i <= n ; i++ ) ++d[ to[ i ] ];
	for( int i = 1 ; i <= n ; i++ )
		if( !d[ i ] )
			dfs( i ), ans2--;
	for( int i = 1 ; i <= n ; i++ )
		if( !vis[ i ] && to[ i ] != i)
			dfs( i ), ans2--;
}

int main()
{
	n = read();
	for( int i = 1 ; i <= n ; i++ ) to[ i ] = read();
	cal_min();
	cal_max();
	return printf( "%d %d\n", ans1, ans2 ), 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值