http://acm.hdu.edu.cn/showproblem.php?pid=6747
Problem Description
我们有一个圈,从内到外一共被分成了 n 个环,中间是空的。
我们把从外到内第 i 层环平分成 a[i] 份,其中 a[i] 是偶数,我们把这 a[i] 份黑白染色,第奇数个染成黑色,第偶数个染成白色。
现在我们旋转每一层,每一层都会等概率随机到一个中止位置。
问黑色的联通块数目的期望。两块黑色的区域有交点即算联通。层之间的旋转是相互独立的。
Input
第一行一个正整数 test(1≤test≤10) 表示数据组数。
对于每组数据,第一行一个正整数 n(1≤n≤10)。
接下来一行 n 个正整数 a[i](2≤a[i]≤1000),a[i] 为偶数,另外保证 a 序列不降。
Output
对于每组数据,一行一个数表示答案,由于答案 A/B 中的 AB 可能很大,请输出 A/Bmod109+7,假设 A/B 为最简分数,A/Bmod109+7=A∗B−1mod109+7,B−1 为满足 B−1∗Bmod109+7=1 的整数。
【分析】
观察得到的性质:黑色的联通块构成了一个森林。
证明:因为序列a单调不降,所以从外而内每一圈被分成的被平分的份数越来越多,即越向内分的越细,外圈的一个黑块可以向内连接多个黑块,内圈的方块向外最多连接一个黑块,是一个树形结构,外圈为父亲节点,内圈为子节点。
对于森林,联通块的个数 = 点数 - 边数,取期望,E(联通块的个数) = E(点数) −E(边数)
考虑第i圈,第i圈共有a[i] / 2个黑点,对E(点数)的贡献为ai / 2,
计算它向第i + 1圈(向内一圈)连边的边数期望E(i),则E(总边数) = E[1] + E[2] + ... + E[n - 1]
第i + 1圈共有(a[i + 1] / 2)个黑点,设随机变量X[i],若第i + 1圈第i个黑点与第i圈的某个黑点联通(即有1条连边),则X[i] = 1,若无则X[i] = 0,
因此E[i] = E( X[1] + X[2] + ... + X[ a[i + 1] / 2 ] ),
根据期望的线性性:
E[i] = E( X[1] ) + E( X[2] ) + ... + E( X[ a[i + 1] / 2 ] ) )
由于旋转对称性
E( X[1] ) = E( X[2] ) = ... = E( X[ a[i + 1] / 2 ] ) )
故E[i] = E( X[1] ) * a[i + 1] / 2
只需计算内圈的一个黑块与外圈连边的期望
内圈的一个黑块(图中红块)的一边在蓝色夹角内活动,蓝色的夹角为2 * Pi / (1 / a[i] * 2),有连边的临界情况即为图中的两个红色部分,所占的角度范围为2*PI * (1 / a[i] + 1 / a[i + 1]),因此有连边的概率为 P(X1)=(2*PI * (1 / a[i] + 1 / a[i + 1])/ (2 * Pi / (1 / a[i] * 2)),则期望E(X1)= P(X1) * 1(代表一条边),所以E[i] = E( X[1] ) * a[i + 1] / 2 = ( a[i] + a[i + 1] )/ 4
最后答案即为 (a[1] + a[n]) / 4
#include<iostream>
#include<sstream>
#include<fstream>
#include<vector>
#include<list>
#include<deque>
#include<queue>
#include<stack>
#include<map>
#include<set>
#include<bitset>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cctype>
#include<cmath>
#include<ctime>
#include<iomanip>
#define f(i,l,r) for(int i=(l);i<=(r);i++)
#define ff(i,r,l) for(int i=(r);i>=(l);i--)
#define fe(i,u) for(int i=h[u];~i;i=e[i].nxt)
#define ll long long
using namespace std;
const int MAXN = 1e6 + 5;
const int MAXM = 1200010;
const int MOD = 1e9 + 7;
const int Inv2 = 500000004;
int n;
int T;
const int Inv4 = 250000002;
ll Pow(ll a, ll b, int MOD)
{
ll res = 1;
while(b){
if(b & 1) res = res * a % MOD;
a = a * a % MOD;
b >>= 1;
}
return res;
}
int main()
{
// freopen("data.in", "r", stdin);
// freopen("test.out", "w", stdout);
cin >> T;
while(T --){
cin >> n;
ll a, b;
cin >> a;
if(n == 1){
cout << a / 2 << endl;
continue;
}
f(i, 2, n){
cin >> b;
}
ll ans = (a + b) / 2;
cout << ans * Inv2 % MOD << endl;
}
return 0;
}