ARC154E 做题记录
很强的题目!
我们求的就是一个数前面比他大的个数 \(-\) 后面比他小的个数。
我们仔细观察,可以注意到两者之间是有关联的。
具体的,对于 \(i\) 和数字 \(p_i\),设 \(x\) 为 \(p_{1...i-1}\) 中比 \(p_i\) 大的个数,那么右边比 \(p_i\) 大的个数为 \(n-p_i-x\),右边比 \(p_i\) 小的个数为 \(n-i-(n-p_i-x)=p_i-i+x\),两者相减得 \(i-p_i\)。
所以我们要算的实际上是 \(E(\sum i(i-p_i))=\sum i^2 - E(\sum i\cdot p_i)\)!
然后我们考虑 \(p_i\) 的最终所在位置的期望,而不是一味的求 \(i\) 位置上最终数字的期望。
当 \(p_i\) 被一次操作区间包含时,我们会发现它达到 \(j\) 和 \(n-j+1\) 的概率都相等。
具体而言,到达位置 \(j\) 的概率为 \(\dfrac{\min(\min(i,j),n-\max(i,j)+1)}{i(n-i+1)}\),最好还是画个图,画个图就知道 \(j\) 和 \(n-j+1\) 的概率相等。
所以被操作后的位置期望是 \(\dfrac{n+1}2\)!
我们只需要算每个 \(p_i\) 至少一次被操作的概率和都没有被操作的概率。
总结一下:
-
抓性质,找到不同东西之间的联系
-
转化为每个数独立计算
-
在更深入的推导中,若被卡壳,别忘了从其他的子角度入手
-
观察操作的特点,并归纳
点击查看代码
#include<bits/stdc++.h>
#define ll long long
#define pir pair<ll,ll>
#define mkp make_pair
#define fi first
#define se second
#define mkp make_pair
#define pb push_back
using namespace std;
const ll maxn=2e5+10, mod=998244353;
ll n,m,a[maxn],ans,res;
ll power(ll a,ll b=mod-2){
ll s=1; a%=mod;
while(b){
if(b&1) s=s*a%mod;
a=a*a%mod; b>>=1;
} return s;
}
int main(){
scanf("%lld%lld",&n,&m);
for(ll i=1;i<=n;i++){
scanf("%lld",a+i);
}
ll d=power(n*(n+1)/2,m);
for(ll i=1;i<=n;i++) ans=(ans+i*i)%mod;
for(ll i=1;i<=n;i++){
ll tmp=((i-1)*i/2+(n-i)*(n-i+1)/2)%mod*power(n*(n+1)/2)%mod;
tmp=power(tmp,m);
res=(res+((mod-mod/2)*(n+1)%mod*(mod+1-tmp)%mod+tmp*i%mod)*a[i])%mod;
}
ans=(ans-res+mod)*d%mod;
printf("%lld",ans);
return 0;
}