【BZOJ】1257 余数之和

来源:互联网 发布:三玻两腔中空玻璃数据 编辑:程序博客网 时间:2024/06/05 13:34
[Analysis]
j(n,k)
=∑k mod i, 1<=i<=n
=∑k-[k/i]*i,1<=i<=n
=n*k-∑[k/i]*i,1<=i<=n
当i>k时,[k/i]*i=0。
∴只用考虑i<=k的情况,即:
j(n,k)=n*k-∑[k/i]*i,1<=i<=min(n,k)。

根据性质知道[k/i]的取值个数不超过根号k个。
又易得f(i)=[k/i]的值是下降的,所以即求根号k个连续区间。

用i从1到min(n,k)枚举,每次找到取值w,算出左右区间。
左区间:L=i。
右区间:当[k/i]=w时,
根据高斯消元的性质,w=[k/i]<=k/i,
∴i>=k/w,R=(int)k/w。
∵可能存在R>=n,所以R=min(R,n)。
对于每个区间,res+=w*∑q,L<=q<=R。
然后i=R+1。

最后的res就是结果。

[Sumup]
①两个高斯记号的取值范围的性质。 [] {}
②[k/i]的取值个数不超过根号k个,因此可以从头开始枚举找到这k个值,时间复杂度O(根号k)。
③a mod b = a - [a/b]*b。

[Code]
/**************************************************************    Problem: 1257    User: y20070316    Language: C++    Result: Accepted    Time:8 ms    Memory:804 kb****************************************************************/ #include <cstdio>#include <cstring>#include <cstdlib>using namespace std; typedef long long LL; int n,m;LL sum; int main(void){       scanf("%d%d",&n,&m);         sum+=(LL)n*m;    if (n>m) n=m;         int l,r,j;    for (int i=1;i<=n;i=r+1)    {        j=m/i,l=m/(j+1)+1,r=m/j;        if (r>=n) r=n;        sum-=(LL)(l+r)*(r-l+1)*j/2;    }    printf("%lld\n",sum);         return 0;}

1 0