Description
有很多n位数(可以有前导0),如果一个n位数X对所有的k(1≤k<n)都满足
X∗10kMod10n>X,这个X我们就认为它脱团了。现在告诉你n,求出有多少个X脱团了。
题目是这样的,设f(n)是n位数里脱团数的数量(脱团数定义如上),现在让你求出f(1)*1^2+f(2)*2^2+…+f(i)*i^2+…+f(n)*n^2 mod 1000000007的结果。
对于100%的数据:n≤10000000000。
所有的一位数都满足,所以f(1)=10。
满足条件的两位数的有01到09,12到19,23到29,34到39,45到49,56到59,67到69,78,79,89总共45个。
Solution
考虑f(n)怎么求
发现它是类似字符串周期的东西
先将这个数首尾相连拼成一个环
可以发现如果循环节长度小于n那么一定不满足(因为从第二个循环节开头开始前面的都一样,但是后面的是0)
那么剩下的串都是循环节长度等于n的(即从哪个位置开头都不一样)
然后我们发现循环的n个字符串中只有字典序最小那个符合要求
那么只需要求出循环节长度为n的,再除以n即为所求f
可以发现如果存在循环节长度为i,那么i的倍数一定也是循环节
每个质因子只用算一次
容斥原理
所以
f(n)=∑d|nμ(d)∗10ndn
那么答案就是
∑i=1ni∑d|iμ(d)∗10nd
交换主体
=∑d=1d∗μ(d)∑i=1⌊nd⌋i∗10i
前面的可以杜教筛来做,具体可以看我的Blog
后面的话,我的SB做法是分治,分成n/2前后两部分处理
然而如果你高中数学过关的话可以用类似等比数列的方法推出来(10S-S)之类的,然后裂项相消
最后推出来是
10n+1∗n−10n+1−1009−109
懒得化简了
复杂度大概是O(N23+N−−√logN)
Code
#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstdlib>
#include <cstring>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define fod(i,a,b) for(int i=a;i>=b;i--)
#define N 5000005
#define LL long long
#define mo 1000000007
#define H 1000007
using namespace std;
LL n,ny,ny9;
int h[N],su[N],mu[N],pr[N],wh[H];
bool bz[N];
void prp()
{
mu[1]=su[1]=1;
fo(i,2,5000000)
{
if(!bz[i])
{
pr[++pr[0]]=i;
mu[i]=-1;
}
for(int j=1;j<=pr[0]&&i*pr[j]<=5000000;j++)
{
bz[i*pr[j]]=1;
if(i%pr[j]==0)
{
mu[i*pr[j]]=0;
break;
}
mu[i*pr[j]]=-mu[i];
}
su[i]=((LL)su[i-1]+(LL)i*mu[i])%mo;
}
}
int hs(LL k)
{
int w=k%H;
while(wh[w]!=k&&wh[w]!=0) w=(w+1)%H;
return w;
}
LL get(LL k)
{
if(k<=5000000) return su[k];
int p=hs(k);
if(wh[p]==k) return h[p];
wh[p]=k,h[p]=1;
LL d=2;
while(d<=k)
{
LL m=k/d,d1=k/m;
h[p]=((LL)h[p]-(d+d1)%mo*((d1-d+1)%mo)%mo*ny%mo*get(m)%mo+mo)%mo;
d=d1+1;
}
return h[p];
}
LL ksm(LL k,LL n)
{
LL s=1;
k=k%mo;
for(;n;k=k*k%mo,n>>=1) if(n&1) s=s*k%mo;
return s;
}
LL doit(LL n)
{
LL ms1=ksm(10,n+1);
return(n%mo*ms1%mo-(ms1-100)%mo*ny9%mo-10+mo)%mo*ny9%mo;
}
int main()
{
LL ans=0;
cin>>n;
ny=ksm(2,mo-2);
ny9=ksm(9,mo-2);
LL d=1;
prp();
while(d<=n)
{
LL m=n/d,d1=n/m,s=0;
ans=(ans+(get(d1)-get(d-1)+mo)%mo*doit(m)%mo+mo)%mo;
d=d1+1;
}
printf("%lld\n",(ans+mo)%mo);
}