题目要求区间[l,r]的每个数的因数,然后计算每个因数最高的数是出现多少次?
那咋写呢?考虑朴素算法..就是枚举[l,r]每个数的因子再对最高位进行统计..但是这样算法的复杂度就到了大于O(r-l)的级别.具体我也不会算.这样肯定不行,考虑优化.怎么优化呢?
首先求[l,r]中区间每个因数最高的数是出现多少次,是不是就等同于 求[1,r]中每个因数最高的数出现多少次-求[1,l-1]中每个因数最高的数出现多少次?那么我们来枚举一个因式从而确定另外一个因式的多少,保证在因式的积在l~r的范围就好了吧?
枚举因数肯定是枚举到根号x就好了...因为之前的都算过了.
#include<bits/stdc++.h> #define ios ios_base::sync_with_stdio(0),cin.tie(0),cout.tie(0) #define pb push_back #define inf 132913423039 typedef long long ll; const ll mod=1e9+7; const ll N=2e5+5; const ll M=10+5; const double eps=1e-7; using namespace std; ll gcd(ll a,ll b) {return b==0?a:gcd(b,a%b);} ll lcm(ll a,ll b) { return a*b/gcd(a,b); } ll qp(ll a,ll b, ll p){ll ans = 1;while(b){if(b&1){ans = (ans*a)%p;--b;}a = (a*a)%p;b >>= 1;}return ans%p;} ll Inv(ll x) { return qp(x,mod-2,mod);} ll C(ll n,ll m){if (m>n) return 0;ll ans = 1;for (int i = 1; i <= m; ++i) ans=ans*Inv(i)%mod*(n-i+1)%mod;return ans%mod;} ll A(ll n,ll m,ll mod){ll sum=1; for(int i=n;i>=n-m+1;i--) sum=(sum*i)%mod; return sum%mod;} ll lowbit(ll x) {return x&(-x);} ll c[N],sum1[N],sum2[N],a[N],lsh[N],n,m; void add1(ll i,ll k){ while(i<=n) {c[i]+=k;i+=lowbit(i);}}//预处理ai单点修改 区间查询***预处理a[i]-a[i-1]区间修改单点查询 ll Sum1(ll i) {ll res=0; while(i>0) res+=c[i],i-=lowbit(i);return res;}//预处理ai单点修改 区间查询***预处理a[i]-a[i-1]区间修改单点查询 void add2(ll i,ll k){ ll x=i;while(i<=n) {sum1[i]+=k;sum2[i]+=k*(x-1);i+=lowbit(i);}}//区间修改,区间查询 ll Sum2(ll i) {ll res=0,x=i;while(i>0){ res+= x * sum1[i]-sum2[i]; i -= lowbit(i);}return res;}//区间修改,区间查询 void ls(){ll cnt; for(ll i=1;i<=n;i++) lsh[i]=a[i]; sort(lsh+1,lsh+n+1);cnt = unique(lsh+1,lsh+n+1)-lsh-1; for(int i=1;i<=n;i++)a[i]=lower_bound(lsh+1,lsh+cnt+1,a[i])-lsh;} ll le[M],ri[M]; ll cnt(ll x) {ll ans; while(x) {ans=x%10;x/=10;}return ans;}//统计最高位是多少. void getans1(ll x) { for(ll i=1;i*i<=x;i++)//枚举一个因数 { ll top=x/i,dep=i+1;//上线和下限. /* 枚举到的最大值是x/i吧. 枚举的最小值肯定是i+1.要求不重复啊! 那么枚举的范围是不是就是x/i-i了呢? 但是这个题目不是问你总数有多少啊!而是问你以某某开头的数有多少.那么我们另外一个因数可以采取枚举开头统计个数 */ for(ll j=1;j<=x;j=j*10)//枚举现在是几位数 { for(ll k=1;k<10;k++)//枚举这位数下以k开头的数最大和最小是多少. { /*这里统计的时候要注意下最大值和最小值*/ //现在这个数的最小值是j*k,那么它的最大值就是(j+1)*k-1 ll mi=max(dep,j*k); ll mx=min(top,(k+1)*j-1); if(mi<=mx)//假如我枚举的最小值大于最大值那么不符合条件的 { le[k]+=mx-mi+1;//另外一个因数个数 } } } le[cnt(i)]+=x/i-i+1;//第一个因数个数 } } void getans2(ll x) { for(ll i=1;i*i<=x;i++)//枚举一个因数 { ll top=x/i,dep=i+1;//上线和下限. /* 枚举到的最大值是x/i吧. 枚举的最小值肯定是i+1.要求不重复啊! 那么枚举的范围是不是就是x/i-i了呢? 但是这个题目不是问你总数有多少啊!而是问你以某某开头的数有多少.那么我们另外一个因数可以采取枚举开头统计个数 */ for(ll j=1;j<=x;j=j*10)//枚举现在是几位数 { for(ll k=1;k<10;k++)//枚举这位数下以k开头的数最大和最小是多少. { /*这里统计的时候要注意下最大值和最小值*/ //现在这个数的最小值是j*k,那么它的最大值就是(j+1)*k-1 ll mi=max(dep,j*k); ll mx=min(top,(k+1)*j-1); if(mi<=mx)//假如我枚举的最小值大于最大值那么不符合条件的 { ri[k]+=mx-mi+1;//另外一个因数个数 } } } ri[cnt(i)]+=x/i-i+1;//第一个因数个数 } } signed main() { ios; ll l,r; cin>>l>>r; getans1(r);getans2(l-1); for(ll i=1;i<=9;i++) cout<<le[i]-ri[i]<<endl; return 0; } //因数i和i的那种情况只能计算一种.