题目
点这里看题目。
分析
好奇怪妙的题目。
你可以首先尝试一下小范围数据暴力,然后找规律。
对,我知道暴力很难写。
算了,丢掉暴力,看一看下面这个非常玄幻优雅的做法:
考虑定义势函数 (phi(u)) 。如果 (u) 的跟随点的数量为 (k) ,则 (phi(u)=2^k-1) 。对于未被选中的点,我们也可以这样定义出它的势函数。
顺便定义整个局面的势 (Phi=sum_{u=1}^n phi(u)) 。
中止局面的势可以很方便地算出来: (Phi_{End}=2^{n-1}-1)
于是,钦定 (u) 和 (v) 是选中的点,它们的跟随点数量分别为 (p) 和 (q) 。考虑它们两个进行操作之后, (Phi) 的期望变化量:
[egin{aligned}E(Delta) &=frac{1}{2}((2^{p+1}-1)-(2^p-1)-(2^q-1))+frac{1}{2}((2^{q+1}-1)-(2^p-1)-(2^q-1))\&=frac{1}{2}(2^{p+1}-2 imes 2^p+2^{q+1}-2 imes 2^q)+1\&=1end{aligned}
]
妙,不可言。
通过简单的计算,我们知道了,任意两个点进行合并,整个局面的 (Phi) 期望变化值为 1 。
那么局面的期望变化次数(变化量除以每次期望变化量)就可以直接计算了:
[Phi_{End}-Phi_{Begin}
]
代码
#include <cstdio>
const int mod = 1e9 + 7;
const int MAXN = 505;
template<typename _T>
void read( _T &x )
{
x = 0;char s = getchar();int f = 1;
while( s > '9' || s < '0' ){if( s == '-' ) f = -1; s = getchar();}
while( s >= '0' && s <= '9' ){x = ( x << 3 ) + ( x << 1 ) + ( s - '0' ), s = getchar();}
x *= f;
}
template<typename _T>
void write( _T x )
{
if( x < 0 ){ putchar( '-' ); x = ( ~ x ) + 1; }
if( 9 < x ){ write( x / 10 ); }
putchar( x % 10 + '0' );
}
int pw[MAXN];
int siz[MAXN];
int N;
void sub( int &x, const int v ) { x = ( x < v ? x - v + mod : x - v ); }
int main()
{
read( N );
for( int i = 1, a ; i <= N ; i ++ ) read( a ), siz[a] ++;
pw[0] = 1; for( int i = 1 ; i <= N ; i ++ ) pw[i] = 2ll * pw[i - 1] % mod;
int ans = pw[N - 1]; sub( ans, 1 );
for( int i = 1 ; i <= N ; i ++ ) sub( ans, pw[siz[i]] - 1 );
write( ans ), putchar( '
' );
return 0;
}