[CF1025G]Company Acquisitions
题目
点这里看题目。
分析
好奇怪妙的题目。
你可以首先尝试一下小范围数据暴力,然后找规律。
对,我知道暴力很难写。
算了,丢掉暴力,看一看下面这个非常玄幻优雅的做法:
考虑定义势函数 \(\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\) 的期望变化量:
\[\begin{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\times 2^p+2^{q+1}-2\times 2^q)+1\\&=1\end{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( '\n' );
return 0;
}