题解 P7327 【Dream and Discs】

· · 题解

题目传送门。

题意简述:有 n 个数 a_1,a_2,\cdots a_n,等概率选取区间 P_1,S_1\subseteq [1,n]P_2\subseteq P_1S_2\subseteq S_1。定义 f(X,Y) 为互不相同的 a_i\ (i\in X,a_i\in Y) 的个数,求 \Delta=F(P_1,S_1)-F(P_2,S_2)等权重平均,即所有可能方案的 \Delta 之和除以方案总数。

挺不错的题目。不过官方题解稍显简略,较难理解,我写个易懂点的。

写在开头的一些定义:

下文中,定义 s_i 表示前 i 个正整数之和,即 \sum_{j=1}^ij;定义 sq_i 表示前 i 个正整数的平方和,即 \sum_{j=1}^ij^2

首先假设 a_i 互不相同,那么显然每个数对答案的贡献独立。对于第 i 个数 a_i,我们单独讨论其贡献。

看似我们已经成功解决了这道题目,但实际上并没有。注意到题目并没有保证 a_i 互不相同。

假设如果有多个相等的数 a_{i_1}=a_{i_2}=\cdots=a_{i_c} 同时满足条件,那么不妨设只有最左边的数会对答案产生贡献。 但即便是这样,也不得不更新一下 f_1,f_2 的求法。

根据上述假设,不难发现如果 a_i=a_j\ (i<j),那么 a_j所有左端点在 i 左边的 P_1(P_2) 都不会产生贡献。因此我们设 f_1'(pre,i) 表示 P_1 左端点在 pre 右侧的总情况数。有了求 f_1 的经验,不难写出 f_1' 的表达式:

f_1'(pre,i)=\sum_{l=pre+1}^i\sum_{r=i}^n\binom{r-l+2}{2}

对比 f_1(i),可以发现 f_1' 相较于 f_1 只有左端点 l 的下界改变,f_1(i)=f_1'(1,i)。类似的,不难得到其化简后的结果,方便 \mathcal{O}(1) 计算。请读者自行推导,此处不再赘述。

因此,a_iF(P_1,S_1) 的贡献可以写为 f_1'(pre_i,i)\times f_1(a_i),其中 pre_ia_i 之前与 a_i 相等的最右边一个数的位置,若不存在则为 0

同理,不难推出 f_2'(pre,i) 的表达式:因为 “嵌套的第一层左括号” 只能在 pre 右边,i 左边,因此可写出 f_2'(pre,i)=\sum_{j=pre+1}^ij。同样的,观察 f_2(i),可以发现 f_2' 相较于 f_2 只有左端点 l 的下界改变,即 f_2(i)=f_2'(1,i)

综上,a_i 对答案的贡献可以写为 f_1'(pre_i,i)\times f_1(a_i)-f_2'(pre_i,i)\times f_2(a_i)

结束了吗?还差一点。求出了 a_i 的总方案数,还需要求 P_1,P_2,S_1,S_2 的总方案数。由于 P_1,P_2S_1,S_2 在定义上等价,因此可以单独算出 P_1,P_2 的总方案数 cnt,再将其平方即可得到四个区间的总方案数。这也是为什么三个样例的分母都是平方数,给分数不约分的良心出题人点赞!

不妨枚举 P_1 的长度 len,则 P_1 共有 n-len+1 种情况,此时其子区间 P_2\binom{len+1}{2} 中情况。根据定义,不难发现 s_i 等价于 \binom{i+1}{2},因此有:

cnt=\sum_{i=1}^n(n-i+1)s_i.

至此,本题被我们解决。

时间复杂度:预处理 \mathcal{O}(n),计算每个数的贡献 n\times\mathcal{O}(1)=\mathcal{O}(n),计算总情况数 \mathcal{O}(n)

因此,总时空复杂度 \mathcal{O}(n)

代码说明:

#include <bits/stdc++.h>
using namespace std;

using ll = long long;
const int mod=1e9+7;
const ll iv2=mod+1>>1;

ll ksm(ll a,ll b){
    ll s=1,m=a;
    while(b){
        if(b&1)s=s*m%mod;
        m=m*m%mod,b>>=1;
    } return s;
} ll inv(ll x){return ksm(x,mod-2);}

const int N=5e5+5;
ll n,a,ans,cnt,buc[N],s[N],sq[N];
ll cals(ll l,ll r){return (s[r]-s[l-1]+mod)%mod;}
ll calsq(ll l,ll r){return (sq[r]-sq[l-1]+mod)%mod;}
ll f1(ll l,ll k){
    return ((calsq(l,k)*(n-k+1)+calsq(k,n)*(k-l+1)
            -2*cals(l,k)*cals(k,n)%mod-3*cals(l,k)*(n-k+1)%mod
            +3*cals(k,n)*(k-l+1)+2*(n-k+1)*(k-l+1))%mod+mod)*iv2%mod;
} ll f2(ll l,ll k){return cals(l,k)*s[n-k+1]%mod;}

int main(){
    cin>>n;
    for(ll i=1;i<=n;i++)s[i]=(s[i-1]+i)%mod,sq[i]=(sq[i-1]+i*i)%mod;
    for(ll i=1,pre;i<=n;i++){
        scanf("%lld",&a),pre=buc[a]+1,buc[a]=i;
        ans=(ans+f1(pre,i)*f1(1,a)-f2(pre,i)*f2(1,a)%mod+mod)%mod;
        cnt=(cnt+(n-i+1)*s[i])%mod;
    } cout<<ans*inv(cnt*cnt%mod)%mod<<endl;
    return 0;
}

长 篇 巨 著。

感觉做完这题水平提升了一大截。

看在这么详细的份上,看懂了就点个赞吧 qwq。