202104-4 校门外的树

举报
辰chen 发表于 2022/06/15 00:05:34 2022/06/15
【摘要】 202104-4 校门外的树 C++总结 本题链接:202104-4 校门外的树 本博客给出本题截图: C++ #include <iostream> #includ...

202104-4 校门外的树


本题链接202104-4 校门外的树

本博客给出本题截图
在这里插入图片描述

C++

#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>

using namespace std;

typedef long long LL;

const int N = 1010, M = 100010, MOD = 1e9 + 7;

int n;
int a[N];
int f[N];
vector<int> q[M];
bool st[M];

int main()
{
    for (int i = 1; i < M; i ++ )
        for (int j = i * 2; j < M; j += i)
            q[j].push_back(i);

    scanf("%d", &n);
    for (int i = 0; i < n; i ++ ) scanf("%d", &a[i]);
    f[0] = 1;
    for (int i = 1; i < n; i ++ )
    {
        memset(st, false, sizeof st);
        for (int j = i - 1; j >= 0; j -- )
        {
            int d = a[i] - a[j], cnt = 0;
            for (int k: q[d])
                if (!st[k])
                {
                    cnt ++ ;
                    st[k] = true;
                }
            st[d] = true;
            f[i] = (f[i] + (LL)f[j] * cnt) % MOD;
        }
    }

    printf("%d\n", f[n - 1]);
    return 0;
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46

总结

典型的 dp + 约数,注意预处理

超级长的题干抽象出来之后该问题为:对于一个起始点为 a[1],终点为 a[n] 的这么一条线段上,你可以选择若干个点,比如我们选择了两个点 a[k] 和 a[j],其中 k < j,那么我们就把这个线段分成了三段,分别为 a[1] ~ a[k],a[k] ~ a[j],a[j] ~ a[n],那么对于这么三段,每一段我们都可以把它进行等分,即存在一个点 t,使得 a[1] ~ t 的长度等于 t ~ a[k],(这是二等分,当然你也可以继续等分下去,当然这里必须保证是约数),并且 t 这个点不可以和 a[s],s ∈ (1, k) 重合,问满足这样约数条件有多少种情况是符合题意的

显然是一道 dp 问题,首先我们需要预处理约数,因为后续涉及到等分的操作,避免后期重复运算直接把范围内的约数都计算出来,放入到 q 中;然后去考虑需要几维的转移,实在不知道几维的话不妨从一维开始试,设 f[i] 表示的是前 i 个障碍点的所有方案,那么显然有初始化 f[0] = 1,然后状态转移:f[i] 可以从 f[0] ~ f[i - 1] 的状态中进行转移,假设 j ∈ [0, i),那么转移过程就变成了前 j 个点的方案数再乘上 a[j] ~ a[i] 这么一段可以划分成的个数,最后的 f[i] 就是对所有的情况求和(注意取模)

本题重中之重还在于 st 数组的使用,因为是要求和的原因,所以我们在不漏的前提下必须加入不重,那么对于 a[i] 而言,我们是从后往前进行遍历的,遍历的过程中,我们需要记录 k 这个长度是否是被用过的,如果用过,我们需要直接 continue,因为对于同一个 a[i] 而言如果我们在 a[r] 时是用过 k 的话,那么我们再次把每份分成长度为 k 的时候,比如此时是 a[l],那么我们 a[l] ~ a[i] 这段一定有一个划分点是 a[r],但是我们题目要求不可以有这种情况,故我们开一个 st 数组,每一个 a[i] 在进行状态转移的时候我们都需要清空一下,其作用就是记录分成长度为 k,这个长度是否被用过,如果没有用过,就让 cnt ++, st[k] = true;,最后记得要让st[a[i] - a[j]] = true;

文章来源: chen-ac.blog.csdn.net,作者:辰chen,版权归原作者所有,如需转载,请联系作者。

原文链接:chen-ac.blog.csdn.net/article/details/120117742

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。