题目链接

https://codeforces.com/contest/1025/problem/G

题解

什么神仙题……
结论:定义一个有 \(k\) 个儿子的点的势能为 \(2^k-1\),一个状态的势能等于所有点的势能总和,答案等于终止状态的势能(\(2^{n-1}-1\))减去初始状态的势能(\(\sum^n_{i=1}2^{sonn_i}-1\))。
证明:考虑每次操作后一个状态的期望势能变化:假设两个自由状态的点儿子个数分别为 \(x,y\),则期望势能变化为 \(\frac{(2^{x+1}-1)+(2^{y+1}-1)}{2}-(2^x-1+2^y-1)=1\). 因此每次操作期望势能变化为 \(1\);设从 \(0\)\(x\) 的期望操作次数为 \(f(x)\),则对于所有的实数 \(x\)\(f(x)\) 的关系可以描述为线性方程组,该方程组有唯一解,且 \(f(x)=x\) 是该方程组的一组解(带入验证),故 \(f(x)=x\).
即总共期望操作次数为末势能减初势能。直接计算即可。
时间复杂度 \(O(n)\).

代码

#include<bits/stdc++.h>
#define llong long long
#define mkpr make_pair
#define iter iterator
#define riter reversed_iterator
#define y1 Lorem_ipsum_dolor
using namespace std;

inline int read()
{
	int x = 0,f = 1; char ch = getchar();
	for(;!isdigit(ch);ch=getchar()) {if(ch=='-') f = -1;}
	for(; isdigit(ch);ch=getchar()) {x = x*10+ch-48;}
	return x*f;
}

const int mxN = 500;
const int P = 1e9+7;
int cnt[mxN+3];
int n;

llong quickpow(llong x,llong y)
{
	llong cur = x,ret = 1ll;
	for(int i=0; y; i++)
	{
		if(y&(1ll<<i)) {y-=(1ll<<i); ret = ret*cur%P;}
		cur = cur*cur%P;
	}
	return ret;
}

int main()
{
	n = read();
	for(int i=1; i<=n; i++) {int x = read(); if(x!=-1) {cnt[x]++;}}
	llong ans = quickpow(2ll,n-1)-1;
	for(int i=1; i<=n; i++) {ans = (ans-quickpow(2ll,cnt[i])+1+P)%P;}
	printf("%I64d\n",ans);
	return 0;
}