[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;
}
posted @ 2020-08-05 22:23  crashed  阅读(149)  评论(0编辑  收藏  举报