题解: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; }