题解 不知道该叫啥
题目描述
【数据范围】
n ≤ 100 , m ≤ 1 0 9 n \leq 100 , m \leq 10^9 n≤100,m≤109
具体做法与心路历程
考场上首先想到的是
O
(
n
m
)
O(nm)
O(nm)的做法。后来推不动了就去看后面的题,后面题也推不动了上了个厕所回来就想出来了。真的要充分利用。还是太菜了,别人10min的切的题,我用了1h
具体做法
按照考试时的时间顺序的想法。
首先观察性质:设已经有了个长度为 n n n的序列,那么求 n + 1 n+1 n+1的序列只与第 n n n个数有关。且设第 n n n个数为 i i i,那么接下来可以填 1 1 1~ M i \frac{M}{i} iM内的所有数。
那么可以设
f
n
,
i
f_{n,i}
fn,i表示长度为
n
n
n的序列第
n
n
n个位置为
i
i
i的方案数。那么有
f
n
,
i
=
∑
k
M
i
f
n
−
1
,
k
f_{n,i}=\sum_{k}^{\frac{M}{i}}{f_{n-1,k}}
fn,i=k∑iMfn−1,k
显然可以
O
(
m
)
O(m)
O(m)转移,但时间复杂度承受不住。
考虑整除分块,对于 M i \frac{M}{i} iM相同的数可以放在同一个块了,因为他们的长度每 + 1 +1 +1的答案是相同的。
设 f i f_i fi表示第 i i i个块的个数, r i = M i r_i=\frac{M}{i} ri=iM,第 i i i 个块的数的范围是 a i a_i ai~ b i b_i bi, l e n i = b i − a i + 1 len_i=b_i-a_i+1 leni=bi−ai+1。
那么如果 b i ≤ r k b_i \leq r_k bi≤rk, f i + = f k × l e n i f_i += f_k \times len_i fi+=fk×leni
根据整除分块的性质, f f f最多有 M \sqrt{M} M个。转移最坏为 O ( m ) O(\sqrt{m}) O(m),还是承受不了。
注意到为区间加,那么可以差分一下(二分差分位置),对于每次 O ( m ) O(\sqrt{m}) O(m)统计答案。(其实差分位置是有规律的,不用二分)。
C o d e \mathcal{Code} Code
/*******************************
Author:galaxy yr
LANG:C++
Created Time:2019年10月24日 星期四 15时43分32秒
*******************************/
#include<cstdio>
#include<algorithm>
#define int long long
using namespace std;
struct IO{
template<typename T>
IO & operator>>(T&res)
{
T q=1;char ch;
while((ch=getchar())<'0' or ch>'9')if(ch=='-')q=-q;
res=(ch^48);
while((ch=getchar())>='0' and ch<='9') res=(res<<1)+(res<<3)+(ch^48);
res*=q;
return *this;
}
}cin;
const int maxn=1e5+10;
const int mod=1e9+7;
int n,m,a[maxn],b[maxn],cnt,len[maxn],f[maxn],r[maxn],tmp[maxn],s[maxn];
void init()
{
for(int l=1,r;l<=m;l=r+1)
{
cnt++;
r=m/(m/l);
a[cnt]=l,b[cnt]=r;len[cnt]=r-l+1;
f[cnt]=len[cnt];
::r[cnt]=m/l;
}
}
int erfen(int val)
{
int l=0,r=cnt,mid,ans=0;
while(l<=r)
{
mid=(l+r)>>1;
if(val>=b[mid])
ans=mid,l=mid+1;
else
r=mid-1;
}
return ans;
}
void dp()
{
for(int i=1;i<=cnt;i++) tmp[i]=0;
for(int k=1;k<=cnt;k++)
{
tmp[1]=(tmp[1]+f[k])%mod;
int y=erfen(r[k])+1;
tmp[y]=(tmp[y]-f[k]+mod)%mod;
}
for(int i=1;i<=cnt;i++)
tmp[i]=(tmp[i]+tmp[i-1])%mod,f[i]=1ll*tmp[i]*len[i]%mod;
}
signed main()
{
//freopen("noname.in","r",stdin);
//freopen("noname.out","w",stdout);
cin>>n>>m;
init();
while(--n)
dp();
long long ans=0;
for(int i=1;i<=cnt;i++)
ans=(ans+f[i])%mod;
printf("%lld\n",ans);
return 0;
}