hdu 7048 Link with EQ
题意:
-
一条长桌有 n n n 个座位
第 1 1 1 个同学随机选 1 1 1 个位置坐下
接下来的同学,选 1 1 1 个离最近同学的距离最大的位置坐下
(choose a position whose shortest distance to other students is maximized. 这句话要多看几遍,防止读假题)
当最远距离为 1 1 1 时(即再坐 1 1 1 个同学会有相邻的情况出现),称桌子满了
求桌子满时,同学数的期望
分析:
-
要求的是期望,那肯定要知道所有的情况
对于这种题目,只看题目描述看不出花来(大佬是能一眼看出法,而我蒟蒻一个…),因此我去手模了 n = 1 n=1 n=1 到 n = 8 n=8 n=8 的情况(手动打表找规律),发现了规律所在:第 1 1 1 个人的位置定下来之后,剩下的人的位置就确定了 (两人之间位置数为偶数的情况,看上去有两种,其实是一样的)
所以问题就转换成了,枚举第 1 1 1 个人选 1 − − n 1--n 1−−n 不同的位置的贡献值并求和,最后输出的是期望对 1 e 9 + 7 1e9+7 1e9+7 模逆
(It can be proved that the a n s w e r answer answer can always be represented as p q \frac{p}{q} qp , where the greatest common factor between p p p and q q q is 1. When outputting the a n s w e r answer answer modulo M M M , you should output such an integer x x x that 0 ≤ x < M 0≤x<M 0≤x<M and x ⋅ q ≡ p ( m o d M x⋅q≡p(mod M x⋅q≡p(modM). 这句话是说答案要对 M M M 求模逆)
-
讲一下如何快速求出n在不同位置的贡献值
令 d p i dp_i dpi 表示在两边都有人,中间位置数为 i i i 时的方案数
令 m i d = ( i + 1 ) / 2 mid=(i+1)/2 mid=(i+1)/2 , d p i = d p m i d − 1 + d p i − m i d + 1 dp_i = dp_{mid-1}+dp_{i-mid}+1 dpi=dpmid−1+dpi−mid+1
这个状态转移比较好理解,脑模一下,位置数为 i + 2 i+2 i+2 的长桌,现在 1 1 1 和 i + 2 i+2 i+2 都坐了人,再去一人,那他必定坐在 ( 1 + i ) / 2 (1+i)/2 (1+i)/2 的位置
现在我们来手模规律所在了,令 n = 8 n=8 n=8 ,第 1 1 1 个人坐在i位置桌子满时的贡献值分别为:
- d p 6 + 2 dp_6+2 dp6+2
- d p 5 + 2 dp_5 +2 dp5+2
- d p 4 + 3 dp_4 +3 dp4+3
- d p 3 + 3 dp_3 + 3 dp3+3
- d p 3 + 3 dp_3 +3 dp3+3
- d p 4 + 3 dp_4 +3 dp4+3
- d p 5 + 2 dp_5 +2 dp5+2
- d p 6 + 2 dp_6 +2 dp6+2
发现了吧(若没发现,再多模几个数)
用前缀和 s [ i ] s[i] s[i] 维护一下 d p dp dp ,那么总的贡献值 a n s = 2 s [ n − 2 ] + 3 n − 4 ans=2s[n-2]+3n-4 ans=2s[n−2]+3n−4
-
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+5, mo=1e9+7;
int binpow(int a,int b=mo-2,int p=mo)
{
int ans=1;
while(b)
{
if(b&1) ans = ans*a%p;
a = a*a%p;
b >>= 1;
}
return ans;
}
int dp[N], s[N];
signed main()
{
ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
for(int i=3;i<=N-5;i++)
{
int mid = (1+i)>>1;
dp[i] = dp[mid-1]+dp[i-mid]+1;
s[i] = (s[i-1]+2*dp[i])%mo;
}
int T;
cin>>T;
while(T--)
{
int n;
cin>>n;
if(n<=2) { cout<<"1"<<endl; continue; } // 2*binpow(2)%mo
if(n==3) { cout<<"666666673"<<endl; continue; } // 5*binpow(3)%mo
int ans = (s[n-2]+3*n-4)%mo;
ans = ans*binpow(n)%mo;
cout<<ans<<endl;
}
return 0;
}