2021“MINIEYE杯”中国大学生算法设计超级联赛(7)Link with EQ

Link with EQ

题目链接

题目大意:有 n n n 个座位,起初第一个人会随机挑选一个位置坐下,后来的人会挑一个离最近的人最远的位置坐下,当无论挑什么位置旁边( + 1 +1 +1 − 1 -1 1 的位置)都已经坐了人的话,结束。

这题的难点在于,每个人会选择一个离最近的人最远的位置,这个位置可能有多个,虽然概率是相等的,但是倘若我们不知道具体的状态我们就算不出来这样的位置有几个,但是我们显然不可能去考虑具体的状态。所以我们就想,是否有定住的状态,也就是先去计算某个人下一次一定会坐某个位置的状态,最终看看这样的状态能否用于计算所有的状态,神奇的是,确实可以。

f i f_i fi 表示一共有 i i i 个凳子,且凳子两端都已经坐了人,其它位置为空,在此为初始状态下的一共可以坐的人的期望 ( i > 1 ) (i>1) (i>1)
那么首先我们有 f 3 = f 4 = 2 f_3=f_4=2 f3=f4=2,对于其它情况,我们设 j = ( i + 1 ) / 2 j=(i+1)/2 j=(i+1)/2 ,显然这样的情况下一个人只会坐中间,则有:
f i = f j + f i + 1 − j − 1 f_i=f_j+f_{i+1-j}-1 fi=fj+fi+1j1
:这里其实对于 i i i 为偶数的情况是可以有两种选择的,但是这两种情况完全一样,概率又相同,所以直接计算某一种即可。

g i g_i gi 表示一共有 i i i 个凳子,且凳子的一端坐了人,其它位置为空,在此为初始状态下的一共可以坐的人的期望
那么我们首先有 g 1 = g 2 = 1 g_1=g_2=1 g1=g2=1 ,对于其它情况,下一个人必然坐另一个端点,则我们有:
g i = f i g_i=f_i gi=fi

h n h_n hn 表示一共有 n n n 个凳子,所有位置都为空,在此为初始状态下一共可以坐的人的期望。
h 1 = 1 h_1=1 h1=1
h n = ∑ i = 1 n 1 n ( g i + g n + 1 − i − 1 ) = 1 n ∑ i = 1 n ( g i + g n + 1 − i − 1 ) = 2 n ( ∑ i = 1 n g i ) − 1 h_n=\sum\limits_{i=1}^n\frac{1}{n}(g_i+g_{n+1-i}-1)=\frac{1}{n}\sum\limits_{i=1}^n(g_i+g_{n+1-i}-1)=\frac{2}{n}(\sum\limits_{i=1}^ng_i)-1 hn=i=1nn1(gi+gn+1i1)=n1i=1n(gi+gn+1i1)=n2(i=1ngi)1

所以我们只要处理出 g i g_i gi 的前缀和,我们就可以求得所有的答案。

#include <bits/stdc++.h>

using namespace std;

typedef long long ll;

const int N = 1e6 + 10;

const ll mod = 1e9 + 7;

int n;

ll inv[N];

ll f[N], sumg[N], h[N];

ll ksm(ll base, ll n) {
	ll ans = 1;
	while(n) {
		if (n & 1ll) ans = ans * base % mod;
		base = base * base % mod;
		n >>= 1ll;
	}
	return ans;
}

void init(int n) {
	inv[1] = 1;
	for (int i = 2; i <= n; ++i) {
		inv[i] = ksm(i, mod - 2);
	}
	f[3] = f[4] = 2;
	for (int i = 5; i <= n; ++i) {
		int j = (i + 1) / 2;
		f[i] = (f[j] + f[i + 1 - j] - 1 + mod) % mod;
	}
	sumg[1] = 1; sumg[2] = 2;
	for (int i = 3; i <= n; ++i) {
		sumg[i] = (sumg[i - 1] + f[i]) % mod;
	}
	h[1] = 1;
	for (int i = 2; i <= n; ++i) {
		h[i] = (2ll * inv[i] % mod * sumg[i] % mod - 1 + mod) % mod;
	}
}

int main() {
#ifndef ONLINE_JUDGE
	freopen("in.txt", "r", stdin);
	freopen("out.txt", "w", stdout);
#endif
	init(1e6);
	int T;
	scanf("%d", &T);
	while(T--) {
		scanf("%d", &n);
		printf("%lld\n", h[n]);
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值