题解 不知道该叫啥
题目描述

【数据范围】
n≤100,m≤109n \leq 100 , m \leq 10^9n≤100,m≤109
具体做法与心路历程
考场上首先想到的是O(nm)O(nm)O(nm)的做法。后来推不动了就去看后面的题,后面题也推不动了上了个厕所回来就想出来了。真的要充分利用。还是太菜了,别人10min的切的题,我用了1h
具体做法
按照考试时的时间顺序的想法。
首先观察性质:设已经有了个长度为nnn的序列,那么求n+1n+1n+1的序列只与第nnn个数有关。且设第nnn个数为iii,那么接下来可以填111~Mi\frac{M}{i}iM内的所有数。
那么可以设fn,if_{n,i}fn,i表示长度为nnn的序列第nnn个位置为iii的方案数。那么有
fn,i=∑kMifn−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)转移,但时间复杂度承受不住。
考虑整除分块,对于Mi\frac{M}{i}iM相同的数可以放在同一个块了,因为他们的长度每+1+1+1的答案是相同的。
设fif_ifi表示第iii个块的个数,ri=Mir_i=\frac{M}{i}ri=iM,第iii 个块的数的范围是aia_iai~bib_ibi,leni=bi−ai+1len_i=b_i-a_i+1leni=bi−ai+1。
那么如果bi≤rkb_i \leq r_kbi≤rk,fi+=fk×lenif_i += f_k \times len_ifi+=fk×leni
根据整除分块的性质,fff最多有M\sqrt{M}M个。转移最坏为O(m)O(\sqrt{m})O(m),还是承受不了。
注意到为区间加,那么可以差分一下(二分差分位置),对于每次O(m)O(\sqrt{m})O(m)统计答案。(其实差分位置是有规律的,不用二分)。
Code\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;
}
本文详细解析了一种复杂度为O(m)的算法解题思路,通过整除分块和差分技巧优化时间复杂度,针对特定数据范围n≤100,m≤109的题目提供高效解决方案。
300

被折叠的 条评论
为什么被折叠?



