Up the Strip

问题:

Up the Strip (simplified version),Up the Strip(Codeforces Round #740)

题意:

给出一个数n,求把n变成1的方案数,对m(m是一个质数)取模。

每次有两种操作,设x为当前数字。

  • 选择1 ≤ \leq k ≤ \leq x-1,使x=x-k;
  • 选择2 ≤ \leq k ≤ \leq x,使x= ⌊ x / k ⌋ \lfloor x/k \rfloor x/k;

数据范围:

easy version中2 ≤ \leq n ≤ \leq 1 0 5 10^5 105 1 0 8 10^8 108 ≤ \leq m ≤ \leq 1 0 9 10^9 109
hard version中2 ≤ \leq n ≤ \leq 1 0 6 10^6 106 1 0 8 10^8 108 ≤ \leq m ≤ \leq 1 0 9 10^9 109

思路:

可以看出来,把n变成1的方案数,就是把1变成n的方案数。两重for循环得到a[n]即可,时间复杂度O( n 2 n^2 n2)

#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const int MAXN=4000010;
 
int a[MAXN];
int f(int n,int MOD){
    a[1]=1;
    for(int i=2;i<=n;i++){
        for(int j=1;j<=i-1;j++){
            a[i]=(a[i]+a[j])%MOD;
        }
        for(int j=2;j<=i;j++){
            a[i]=(a[i]+a[i/j])%MOD;
        }
    }
    return a[n];
}
 
int main(){
    int n,m;
    cin>>n>>m;
    cout<<f(n,m);
    return 0;
}

结果,到了第四个点,不出所料地TLE了。。。

在这里插入图片描述
考虑优化。

第二重for循环,前面一项 ∑ j = 1 i − 1 a [ i ] \sum_{j=1}^{i-1} {a[i]} j=1i1a[i]可以用前缀和优化,每次加上sum[i-1]即可;后面一项,每次都是向下取整,真正的除数只有 i \sqrt[]{i} i 个,可以用整除分块来做,总体时间复杂度O(n n \sqrt[]{n} n ),2× 1 0 5 10^5 105数据没问题,跑满1秒 1 0 8 10^8 108次运算。

#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const int MAXN=4000010;
 
// int a[MAXN];
// int f(int n,int MOD){
//     a[1]=1;
//     for(int i=2;i<=n;i++){
//         for(int j=1;j<=i-1;j++){
//             a[i]=(a[i]+a[j])%MOD;
//         }
//         for(int j=2;j<=i;j++){
//             a[i]=(a[i]+a[i/j])%MOD;
//         }
//     }
//     return a[n];
// }
 
int a[MAXN],sum[MAXN];
int f(int n,int MOD){
    a[1]=1;
    sum[1]=1;
    for(int i=2;i<=n;i++){
        for(int l=1,r;l<=i;l=r+1){
            r=i/(i/l);
            a[i]=(a[i]+1ll*(r-l+1)*a[i/l])%MOD;
        }
        
        a[i]=(a[i]+sum[i-1])%MOD;
        sum[i]=(sum[i-1]+a[i])%MOD;
    }
    return a[n];
}
 
int main(){
    int n,m;
    cin>>n>>m;
    cout<<f(n,m);
    return 0;
}

在这里插入图片描述
在这里插入图片描述

然而,在hard version的2 ≤ \leq n ≤ \leq 1 0 6 10^6 106数据下,在第四个点就TLE了

在这里插入图片描述

继续考虑优化。

把1变成n的方案优化不动了,考虑把n变成1的方案。这个时候a[k]的意义就是把n变成k的方案数。

每次枚举除数j,也就是i可以从[ji,ji+j-1]除以j得到,时间复杂度为O(n×( 1 2 \frac{1}{2} 21+ 1 3 \frac{1}{3} 31+ 1 4 \frac{1}{4} 41+…+ 1 n \frac{1}{n} n1)),即O(n l o g n log_n logn),4× 1 0 6 10^6 106数据没问题。

#include<iostream>
#include<cstdio>
using namespace std;
typedef long long ll;
const int MAXN=4000010;
 
// int a[MAXN];
// int f(int n,int MOD){
//     a[1]=1;
//     for(int i=2;i<=n;i++){
//         for(int j=1;j<=i-1;j++){
//             a[i]=(a[i]+a[j])%MOD;
//         }
//         for(int j=2;j<=i;j++){
//             a[i]=(a[i]+a[i/j])%MOD;
//         }
//     }
//     return a[n];
// }
 
// int a[MAXN],sum[MAXN];
// int f(int n,int MOD){
//     a[1]=1;
//     sum[1]=1;
//     for(int i=2;i<=n;i++){
//         for(int l=1,r;l<=i;l=r+1){
//             r=i/(i/l);
//             a[i]=(a[i]+1ll*(r-l+1)*a[i/l])%MOD;
//         }
        
//         a[i]=(a[i]+sum[i-1])%MOD;
//         sum[i]=(sum[i-1]+a[i])%MOD;
//     }
//     return a[n];
// }
 
int a[MAXN],sum[MAXN];
int f(int n,int MOD){
    a[n]=1;
    sum[n]=1;
    for(int i=n-1;i>=1;i--){
        a[i]=sum[i+1];
        for(int j=2;j*i<=n;j++){
            int r=min(n,j*i+j-1);
            a[i]=(1ll*a[i]+sum[j*i]-sum[r+1]+MOD)%MOD;
        }
        sum[i]=(sum[i+1]+a[i])%MOD;
    }
    return a[1];
}
 
int main(){
    int n,m;
    cin>>n>>m;
    cout<<f(n,m);
    return 0;
}

函数里,有一个地方需要注意

sum[i]=(sum[i+1]+a[i])%MOD;
//这里的sum[i]由于取模,可能会使后面的sum[j*i]<sum[r+1],要在"sum[j*i]-sum[r+1]"后面加上MOD保证a[i]为正数
a[i]=(1ll*a[i]+sum[j*i]-sum[r+1]+MOD)%MOD;
//这里四个int型的数相加,MOD的范围是1e9,所以可能会发生溢出,要在前面乘以1ll

在这里插入图片描述

在这里插入图片描述

一个问题,三个相同名字相同形参的函数。

所用的空间相同,但第三个函数跑一秒就能解决的问题第一个函数得跑1个小时。

或许,这就是算法的魅力吧。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值