AtCoder Beginner Contest 132 F

F - Small Products

怎么说呢?目前超了自己水平两个档次的题。
第一个档次熟练掌握 d p dp dp
第二个档次熟练掌握分块

这道题:参考大佬

要求求相邻元素乘积 ≤ N ≤N N的序列数。 N ≤ 1 e 9 N≤1e9 N1e9

序列数考虑 d p dp dp
显然: d p [ i ] [ j ] = ∑ x ∗ j ≤ N d p [ i − 1 ] [ x ] dp[i][j]=\sum_{x*j≤N}dp[i-1][x] dp[i][j]=xjNdp[i1][x]
但是显然第二维大小不符合要求。

这种 1 e 9 1e9 1e9一般都是分块或者数学(数学一般是 1 e 18 1e18 1e18)
这道题是分块的做法:值得学习。

我们考虑前 N \sqrt{N} N 个可以直接利用,后面的部分可以划分成 N \sqrt{N} N 块。
i i i块代表了使得 i ∗ k ≤ N i*k≤N ikN成立且 i i i唯一的所有 k k k
意思就是说这里的 k k k只能和 i i i组合,与 i + 1 i+1 i+1不能组合(在后面的式子可以发现被去除了),与 i − 1 i-1 i1组合的部分在前面已经有了。

i i i块的数量为 N i − N i + 1 \frac{N}{i}-\frac{N}{i+1} iNi+1N
数学证明并不会,但举例子比较容易懂。
比如说第一块,只能被 1 1 1唯一整除的。
N / 1 = N N/1=N N/1=N是不考虑唯一的情况
N / 2 N/2 N/2是不考虑唯一,被 2 2 2整除的。
唯一被 1 1 1整除的显然是 N − N / 2 N-N/2 NN/2。以此类推。

接下来是正戏。
我们考虑分类讨论:
第一部分是第 i i i位放的是 N \sqrt{N} N 以内的。 S [ i ] [ j ] S[i][j] S[i][j]
第二部分是第 i i i位放的是以外的,这个时候要利用分块处理。 B [ i ] [ j ] B[i][j] B[i][j]

答案显然是 ∑ i = 1 N S [ k ] [ i ] + B [ k ] [ i ] \sum_{i=1}^{\sqrt{N}} S[k][i]+B[k][i] i=1N S[k][i]+B[k][i]
S [ i ] [ j ] S[i][j] S[i][j]怎么用 d p dp dp解决呢,考虑第 j j j位放的是小的数,那么第 j − 1 j-1 j1位可以放小的数和大的数。
小的数无所谓,对于大的数只能从 [ j , N ] [j,\sqrt{N}] [j,N ]的块用,小于的会导致数过大,超过 N N N.
所以 S [ i ] [ j ] = ∑ k = 1 N S [ i − 1 ] [ k ] + ∑ k = j N B [ i − 1 ] [ k ] S[i][j]=\sum_{k=1}^{\sqrt{N}} S[i-1][k]+\sum_{k=j}^{\sqrt{N}} B[i-1][k] S[i][j]=k=1N S[i1][k]+k=jN B[i1][k]
对于 B [ i ] [ j ] B[i][j] B[i][j],前一位只能放小数,不过这里的 j j j表示的是块,所以还需要乘上块数里的数量。
所以 B [ i ] [ j ] = ( N i − N i + 1 ) ∗ ∑ k = 1 j S [ i − 1 ] [ k ] B[i][j]=(\frac{N}{i}-\frac{N}{i+1})*\sum_{k=1}^{j} S[i-1][k] B[i][j]=(iNi+1N)k=1jS[i1][k]超过 j j j的显然会超。

这样子做 d p dp dp复杂度是 k ∗ N ∗ N k*\sqrt{N}*\sqrt{N} kN N
考虑加的操作,可以简化为前缀和,利用前缀和可以实现 k ∗ N k*\sqrt{N} kN
有个小细节就是第 N \sqrt{N} N 块和放 N \sqrt{N} N 等价。取其一即可。

#include<bits/stdc++.h>
#define FOR(i,l,r) for(int i=l;i<=r;i++)
#define ll long long
using namespace std;

const ll mod = 1e9+7;

ll n;int k;
ll S[110][202000],B[110][202000];

int main(){
    cin>>n>>k;
    ll d=sqrt(n);
    ll ans=0;
    S[0][1]=1;
    FOR(i,1,k){
        FOR(j,1,d)S[i-1][j]=(S[i-1][j]+S[i-1][j-1])%mod;
        FOR(j,1,d)B[i-1][j]=(B[i-1][j]+B[i-1][j-1])%mod;
        FOR(j,1,d){
            S[i][j]=(S[i-1][d]+B[i-1][d]-B[i-1][j-1]+mod)%mod;
            B[i][j]=(n/j-n/(j+1))*S[i-1][j]%mod;
            if(j==n/j)B[i][j]=0;//SжагаСЫ
        }
    }
    FOR(i,1,d)ans=(ans+S[k][i]+B[k][i])%mod;
    cout<<ans<<endl;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值