cf1561D Up the Strip(D1&&D2)

Posted Jozky86

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了cf1561D Up the Strip(D1&&D2)相关的知识,希望对你有一定的参考价值。

cf1561D Up the Strip(D1&&D2)

题意:

一个长度为n的赛道,一开始在n的位置,你要前往到1,每次移动你有两种方式:

  1. 在1和x-1之间选择一个整数y,并从位置x移动到位置x-y
  2. 在2和x之间选择一个整数z,从位置x移动到位置 ⌊ x z ⌋ \\lfloor \\frac{x}{z} \\rfloor zx

问有多少移动方法:
问题D1:n的数据范围是2e5
问题D1:n的数据范围是4e6

D1

题解:

对于第一个转移,任何一个状态都可以转移到x,因为是线性递推的
而对于第二个转移,我们可以发现 ⌊ x z ⌋ \\lfloor \\frac{x}{z} \\rfloor zx在一个区间内值是稳定不变的,这不就是整除分块
因为z∈[2,x],所以整除分块l的初始值为2
知道l,根据整除分块可知 r = i / ( i / l ) r=i/(i/l) r=i/(i/l)
对于这一整个区间i∈[l,r],他们的值 ⌊ n i ⌋ \\lfloor \\frac{n}{i} \\rfloor in的值是一样,所以可以这一整段区间的值,都可以由dp[n/i]转移过来
所以有转移方程:
dp[x]= ∑ i = 1 x − 1 d p [ i ] + ∑ d p [ x l ] \\sum_{i=1}^{x-1}dp[i]+\\sum dp[\\frac{x}{l}] i=1x1dp[i]+dp[lx]
前者我用树状数组维护
复杂度: n n n\\sqrt{n} nn

代码:

// Problem: D1. Up the Strip (simplified version)
// Contest: Codeforces - Codeforces Round #740 (Div. 2, based on VK Cup 2021 - Final (Engine))
// URL: https://codeforces.com/contest/1561/problem/D1
// Memory Limit: 128 MB
// Time Limit: 6000 ms
// Data:2021-08-25 00:01:00
// By Jozky
#include <bits/stdc++.h>
#include <unordered_map>
#define debug(a, b) printf("%s = %d\\n", a, b);
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
clock_t startTime, endTime;
//Fe~Jozky
const ll INF_ll= 1e18;
const int INF_int= 0x3f3f3f3f;
void read(){};
template <typename _Tp, typename... _Tps> void read(_Tp& x, _Tps&... Ar)
{
    x= 0;
    char c= getchar();
    bool flag= 0;
    while (c < '0' || c > '9')
        flag|= (c == '-'), c= getchar();
    while (c >= '0' && c <= '9')
        x= (x << 3) + (x << 1) + (c ^ 48), c= getchar();
    if (flag)
        x= -x;
    read(Ar...);
}
template <typename T> inline void write(T x)
{
    if (x < 0) {
        x= ~(x - 1);
        putchar('-');
    }
    if (x > 9)
        write(x / 10);
    putchar(x % 10 + '0');
}
void rd_test()
{
#ifdef LOCAL
    startTime= clock();
    freopen("in.txt", "r", stdin);
#endif
}
void Time_test()
{
#ifdef LOCAL
    endTime= clock();
    printf("\\nRun Time:%lfs\\n", (double)(endTime - startTime) / CLOCKS_PER_SEC);
#endif
}
const int maxn= 2e5 + 9;
ll a[maxn];
ll f[maxn];
ll n, mod;
ll lowbits(ll x)
{
    return x & (-x);
}
void update(int pos, ll val)
{
    for (int i= pos; i < maxn; i+= lowbits(i)) {
        a[i]= (a[i] + val) % mod;
    }
}
ll query(int pos)
{
    ll val= 0;
    for (int i= pos; i; i-= lowbits(i)) {
        val= (val + a[i]) % mod;
    }
    return val;
}
int main()
{
    //rd_test();

    cin >> n >> mod;

    for (int i= 1; i <= n; i++) {
        if (i == 1) {
            f[i]= 1;
            update(i, f[i]);
            continue;
        }
        f[i]= query(i - 1);
        int r;

        for (int l= 2; l <= i; l= r + 1) {
            r= i / (i / l);
            int R= min(r, i);
            int len= R - l + 1;
            f[i]= (f[i] + 1ll * len * f[i / l] % mod) % mod;
        }
        update(i, f[i]);
    }
    printf("%lld\\n", f[n] % mod);
    return 0;
    //Time_test();
}

D2

题解:

这个题的数据大了20,很明显 n n n\\sqrt{n} nn 过不了
现在对于4e6的数据,很明显我们要优化成 n log ⁡ n n\\log{n} nlogn的做法
对于一个数i,那么某种倍数j,会让 [ i ∗ j , i ∗ j + i ) [i*j,i*j+i) [ij,ij+i)这个范围内都可以移动到i位置
当然还要注意边界情况: i ∗ j < = n 且 j ∗ i + i < = n + 1 i*j<=n且j*i+i<=n+1 ij<=nji+i<=n+1
转移方程为:
dp[i]= ∑ j = i + 1 n d p [ j ] + ∑ i = 1 i ∗ j < = n ∑ k = i ∗ j i ∗ j + j − 1 d p [ k ] \\sum_{j=i+1}^{n}dp[j]+\\sum_{i=1}^{i*j<=n} \\sum_{k=i*j}^{i*j+j-1} dp[k] j=i+1ndp[j]+i=1ij<=nk=ijij+j1dp[k]
枚举倍数的时间复杂度是 O ( l o g n ) O(log n) O(logn)
总复杂度是 n log ⁡ n n\\log{n} nlogn

代码:

// Problem: D1. Up the Strip (simplified version)
// Contest: Codeforces - Codeforces Round #740 (Div. 2, based on VK Cup 2021 - Final (Engine))
// URL: https://codeforces.com/contest/1561/problem/D1
// Memory Limit: 128 MB
// Time Limit: 6000 ms
// Data:2021-08-25 00:01:00
// By Jozky
#include <bits/stdc++.h>
#include <unordered_map>
#define debug(a, b) printf("%s = %d\\n", a, b);
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> PII;
clock_t startTime, endTime;
//Fe~Jozky
const ll INF_ll= 1e18;
const int INF_int= 0x3f3f3f3f;
void read(){};
template <typename _Tp, typename... _Tps> void read(_Tp& x, _Tps&... Ar)
{
    x= 0;
    char c= getchar();
    bool flag= 0;
    while (c < '0' || c > '9')
        flag|= (c == '-'), c= getchar();
    while (c >= '0' && c <= '9')
        x= (x << 3) + (x << 1) + (c ^ 48), c= getchar();
    if (flag)
        x= -x;
    read(Ar...);
}
template <typename T> inline void write(T x)
{
    if (x < 0) {
        x= ~(x - 1);
        putchar('-');
    }
    if (x > 9)
        write(x / 10);
    putchar(x % 10 + '0');
}
void rd_test()
{
#ifdef LOCAL
    startTime= clock();
    freopen("in.txt", "r", stdin);
#endif
}
void Time_test()
{
#ifdef LOCAL
    endTime= clock();
    printf("\\nRun Time:%lfs\\n", (double)(endTime - startTime) / CLOCKS_PER_SEC);
#endif
}
const int maxn= 5e6 + 9;
ll a[maxn];
ll f[maxn];
int n;
ll mod;
ll lowbits(ll x)
{
    return x & (-x);
}
void update(int pos, ll val)
{
    for (int i= pos; i < maxn; i+= lowbits(i)) {
        a[i]= (a[i] + val) % mod;
    }
}
ll query(int pos)
{
    ll val= 0;
    for (int i= pos; i; i-= lowbits(i)) {
        val= (val + a[i]) % mod;
    }
    return val;
}
ll sum以上是关于cf1561D Up the Strip(D1&&D2)的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces Round #740 (Div. 2) D1. Up the Strip (simplified version)

筛法应用CF1558B - Up the Strip

筛法应用CF1558B - Up the Strip

筛法应用CF1558B - Up the Strip

D2. Up the Strip(递推)

整除分块前缀和DPD. Up the Strip(简单版 + 正常版)