Description
有n个人,每个人手里有一把手枪。一开始所有人都选定一个人瞄准(有可能瞄准自己)。然后他们按某个顺序开枪,且任意时刻只有一个人开枪。因此,对于不同的开枪顺序,最后死的人也不同。
Input
输入n人数<1000000 每个人的aim
Output
你要求最后死亡数目的最小和最大可能
Sample Input
8
2 3 2 2 6 7 8 5
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;
}