题解:P10704 救赎(Redemption)

数论题,先看数据范围,发现 $n$ 和 $m$ 都非常大,但是 $\sum_{i=1}^{i=n}a_i \le 10^9$。

解以上不等式得不同的 $a_i$ 大约有 $40000$ 个。记有 $cnt$ 个不同的 $a_i$,所以显然有一种 $O(k^2)$ 的做法。

期望得分:$70$ 分。

考虑优化,设去重后数组为 $w$,观察数据可得不同的 $a_i \times a_j$ 有可能再向下取整后变成一样的值。

先将 $w$ 排序,对于每个 $w_i$,首先确定
$\lfloor \frac{m}{w_iw_j} \rfloor$ 相同时 $j$ 的取值范围。观察发现,$\lfloor \frac{m}{w_iw_j} \rfloor$ 相同时 $j$ 是连续的。所以考虑二分求得左右边界,通过前缀和维护这段区间内的数的总个数,可求得该段的贡献。

题目背景好评

Code:

signed main()
{
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
    }
    sort(a+1,a+n+1);
    for(int i=1;i<=n;i++)
    {
        if(a[i]!=a[i-1])
        {
            b[++cnt1]=a[i];
            cnt[cnt1]=1;
        }
        else
        {
            cnt[cnt1]++;
        }
    }
    for(int i=1;i<=cnt1;i++)
    {
        sum1[i]=sum1[i-1]+cnt[i];
    }
    int now=0;
    int l3=0,r3=0;
    for(int i=1;i<=cnt1;i++)
    {
        sum=(sum+1ll*(m/(b[i]*b[i]))*cnt[i]%mod*cnt[i])%mod;
        if(i==cnt1)
        {
            continue;
        }
        l3=i+1;
        now=(m/(b[i]*b[l3]));
        while(l3<=cnt1)
        {
            r3=check(i,now,l3);
            ret=(ret+now*cnt[i]%mod*(sum1[r3]-sum1[l3-1]))%mod;
            l3=r3+1;
            if(l3>cnt1)
            {
                break;
            }
            now=m/(b[i]*b[l3]); 
        }
    }
    cout<<(ret*2+sum)%mod;
    return 0;
}

 

posted @ 2024-12-14 21:53  _Acheron  阅读(1)  评论(0编辑  收藏  举报