[数论记录]AT3884 [ARC090D] Number of Digits
command_block
2021-05-17 08:14:50
**题意** : 记 $f(x)=\lfloor \lg x\rfloor$ ,即 $x$ 在十进制下的位数。
给出 $S$ ,求满足下列条件的数对 $(l,r)$ 的个数 :
- $1\leq l\leq r$
- $\sum\limits_{i=l}^rf(i)=S$
答案对 $10^9+7$ 取模。
$S\leq 10^8$ ,时限$\texttt{2s}$。
------------
这里有一个 $S\leq 10^{10}$ 且 $50$ 组数据的加强版 : [Link](https://www.luogu.com.cn/problem/U150370)
由于 $f$ 是单调递增的上凸函数,在合法方案中,当 $f(l)$ 增大时, $f(r)-f(l)$ 减小。
- **Case 1** : $f(r)-f(l)=0$
枚举 $f$ 的值。$f(x)=k$ 的 $x$ 的数目为 $9\times 10^{k-1}$。
若 $k$ 是 $S$ 的因数,则选取一个长为 $|S|/k$ 的区间即可满足题意。
故方案数为 :
$$\sum\limits_{k|S}\max\Big\{9\times 10^{k-1}-|S|/k+1\ ,0\Big\}$$
大力求因数,复杂度为 $O(\sqrt{S})$。
- **Case 2** : $f(r)-f(l)=1$
枚举 $f(l)=k$。设区间中 $f(x)=k$ 的有 $a$ 个,$f(x)=k+1$ 的有 $b$ 个。
为了与 Case 1 区分,这里要求 $a,b>0$ 。且由于个数限制也有 $a\leq 9\times 10^{k-1},\ b\leq 9\times 10^k$。
化式子 :
$$
\begin{aligned}
S&=a*k+b*(k+1)\\
&=(a+b)k+b\\
b&=S-(a+b)k
\end{aligned}
$$
注意到当确定 $a+b$ 之后就能确定 $a,b$ ,故只需计数合法的 $a+b$ 的个数。
由 $0<b<a+b$ ,可得 $0<S-(a+b)k<a+b$ 。
解得 $\big\lfloor\tfrac{S}{k+1}\big\rfloor+1\leq (a+b)\leq \big\lfloor\tfrac{S-1}{k}\big\rfloor$。
还要考虑 $a,b$ 的上界,将上界代入并用得到的 $a+b$ 修正上述范围。
注意到当 $k>\lg S$ 时,这两个条件必定能满足,所以只有很小一部分 $k$ 需要修正。
其他的部分为
$$
\begin{aligned}
&\sum\limits_{k\geq lim}\big\lfloor\tfrac{S-1}{k}\big\rfloor-\big\lfloor\tfrac{S}{k+1}\big\rfloor\\
=&\lfloor\tfrac{S}{lim}\big\rfloor+\sum\limits_{k\geq lim}\big\lfloor\tfrac{S-1}{k}\big\rfloor-\big\lfloor\tfrac{S}{k}\big\rfloor\\
=&\lfloor\tfrac{S}{lim}\big\rfloor-\sum\limits_{k\geq lim}[k|S]\\
\end{aligned}
$$
也只需统计因数。
- **Case 3** : $f(r)-f(l)>1$
枚举 $f(r)-f(l)=c$ ,枚举 $f(l)=k$ ,则 $f(r)=k+c$。
由于此时我们跨越了至少一整段,$k,c$ 的范围都是 $O(\lg S)$ ,枚举量是很小的。
设区间中 $f(x)=k$ 的有 $a$ 个,$f(x)=k+c$ 的有 $b$ 个。
此时仍然有 $0<a\leq 9\times 10^{k-1},\ 0<b\leq 9\times 10^{k+c-1}$。
计算出中间整段的 $f$ 的和 $M$ ,记 $S'=S-M$。则有 $S'=ak+b(k+c)$ 。
问题转化为 : 给出不定方程 $c=ax+by$ ,求解 $x,y$ (在一定范围内)的对数。
记 $d=\gcd(a,b)$ ,若 $d\!\not|\ c$ ,则无解。否则将 $c,a,b$ 除以 $d$ 转化为 $a\perp b$ 的情况。
用 $\rm EXgcd$ 得到特解 $x_*,y_*$ (注意可能无解),则通解为 $\begin{cases}x=x_*+kb\\y=y_*-ka\end{cases}$
将范围不等式的四个边界分别代入,即可得知方案数。
这部分的复杂度为 $O\big({\rm poly}(\log S)\big)$
细节真多……
```cpp
#include<algorithm>
#include<cstdio>
#define ll long long
using namespace std;
const int mod=1000000007;
ll powM(ll a,ll t){
ll ret=1;t%=(mod-1);
while(t){
if (t&1)ret=ret*a%mod;
a=a*a%mod;t>>=1;
}return ret;
}
void exgcd(ll a,ll b,ll &x,ll &y){
if (b==0){x=1;y=0;return ;}
exgcd(b,a%b,y,x);y-=(a/b)*x;
}
int gcd(int a,int b)
{return !b ? a : gcd(b,a%b);}
ll n,lim[15];
int main()
{
scanf("%lld",&n);
lim[1]=9;for (int i=2;i<=13;i++)lim[i]=lim[i-1]*10;
ll ans=0;
for (int i=1;1ll*i*i<=n;i++)
if (n%i==0){
ll d=i;
if (d>12)ans--;
if (d>12||n/d<=lim[d])
ans=(ans+9*powM(10,d-1)-n/d+1)%mod;
if (1ll*i*i==n)continue;
d=n/i;
if (d>12)ans--;
if (d>12||n/d<=lim[d])
ans=(ans+9*powM(10,d-1)-n/d+1)%mod;
}
for (int k=1;k<=12;k++){
ll l=max(n/(k+1)+1,(n-lim[k+1]*(k+1)+k-1)/k+lim[k+1])
,r=min((n-1)/k,(n-lim[k]*k)/(k+1)+lim[k]);
ans=(ans+((r>=l) ? r-l+1 : 0))%mod;
}ans=(ans+n/13)%mod;
for (int kl=1;kl<=12;kl++)
for (int kr=kl+2;kr<=12;kr++){
ll s=n;
for (int j=kl+1;j<kr;j++)s-=lim[j]*j;
int d=gcd(kl,kr);
if (s%d||s<=0)continue;
s/=d;int a=kl/d,b=kr/d;
ll limx=lim[kl],limy=lim[kr],x,y,xl,xr;
exgcd(a,b,x,y);x*=s;y*=s;
x=(x%b+b)%b;xl=!x ? b : x;
y=(y%a+a)%a;if (!y)y=a;xr=(s-b*y)/a;
x+=limx/b*b+b;while (x>limx)x-=b;xr=min(xr,x);
y+=limy/a*a+a;while (y>limy)y-=a;xl=max(xl,(s-b*y)/a);
if (xl<=xr)ans+=(xr-xl)/b+1;
}
printf("%lld",(ans+mod)%mod);
return 0;
}
```