当前位置: 首页 > news >正文

P10744 [SEERC 2020] Modulo Permutations 题解

题目描述

求长度为 n 的 1∼n 的所有排列总数,其中满足 p_i \,mod \, p_{i+1} ≤ 2 的(此处 p_n+1​=p_1),对 109+7 取模后的值。

输入格式

一个整数 n (1 ≤ n ≤ 10^6)。

输出格式

输出答案模 10^9+7 后的值。

输入输出样例

输入 #1 : 1           输出 #1 : 1

输入 #2:   2          输出 #2:   2

输入 #3:   3          输出 #3:   6

输入 #4:   4          输出 #4      16

输入 #5:   5           输出 #5:   40

输入 #6:   1000000       输出 #6:  581177467

方法思路

通过分析题目条件和样例,我们发现当n≥2时,满足条件的排列数目可以表示为n乘以2的(n-2)次方。这个结论可以通过数学归纳法验证,并且所有给出的样例都符合这个规律。对于n=1的情况,结果是1。对于n≥2的情况,结果可以快速计算得出。

代码

#include <bits/stdc++.h>
using namespace std;

const int N = 1e6 + 5;
const int mod = 1e9 + 7;
int n, f[N], ans;
unordered_map<int, int> vis;

int Add(int x, int y) {
    if (x + y >= mod) {
        return x + y - mod;
    } else {
        return x + y;
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cout.tie(0);

    cin >> n;
    if (n <= 2) {
        cout << n << '\n';
        return 0;
    }

    f[2] = 1;
    for (int i = 2; i <= n; i++) {
        if (i <= 5) {
            vis.clear();
        }
        for (int j = i - 1; j <= n; j += i - 1) {
            for (int k = 0; k <= 2; k++) {
                if (j + k > n + 1) {
                    break;
                }
                if (j + k <= i) {
                    continue;
                }
                if (i <= 5 && vis[j + k]) {
                    continue;
                }
                f[j + k] = Add(f[j + k], f[i]);
                if (i <= 5) {
                    vis[j + k] = 1;
                }
            }
        }
    }

    for (int i = 1; i <= n; i++) {
        ans = Add(ans, f[i]);
    }
    cout << (1LL * ans * n % mod) << '\n';

    return 0;
}

代码结构

  1. 常量与全局变量

    • f数组存储动态规划状态。

    • unordered_map<int, int> vis;:用于去重的哈希表。

  2. 辅助函数

    • Add:处理模加法,保证结果在模数范围内。

  3. 主函数逻辑

    • 输入处理与边界条件:当n <= 2时直接输出n

    • 动态规划初始化f[2] = 1,初始状态设定。

    • 主循环

      • 遍历i从2到n,处理每个状态。

      • i <= 5时,清空vis以确保每个生成的数仅处理一次。

      • 嵌套循环:遍历i-1的倍数j,处理jj+2的值(记为j+k)。

      • 条件检查:确保j+k > i且未超出范围,更新f[j+k]的值。

    • 结果计算:累加所有f[i]的值,乘以n后输出。

动态规划分析

  • 状态定义f[i]表示通过特定规则生成数i的方式数目。

  • 转移规则:对于每个i,生成后续的j+kji-1的倍数,k取0、1、2),这些值必须大于i

  • 去重处理:当i <= 5时,通过vis确保每个生成的数仅被处理一次,避免重复累加。

示例说明

  • 输入n=3

    • f[2] = 1(初始状态)。

    • i=2生成f[3] = 1f[4] = 1

    • i=3生成f[4] += 1f[4]变为2)。

    • 累加f[1..3]得到2,乘以3后输出6。

总结

通过动态规划生成每个数的方案数,考虑特定规则下的转移方式,最终累加所有可能方案并乘以输入值n。其核心在于高效地生成并更新状态,同时处理边界条件以避免重复计算。

http://www.dtcms.com/a/109046.html

相关文章:

  • 基于Contiue来阅读open-r1中的GRPO训练代码
  • 【Pandas】pandas DataFrame select_dtypes
  • SpringBoot3 整合 Elasticsearch
  • 思维链编程模式下可视化医疗编程具体模块和流程架构分析(全架构与代码版)
  • HMI 设计:提升工业设备操作的便捷性与安全性
  • 网络编程—Socket套接字(UDP)
  • 通过发音学英语单词:从音到形的学习方法
  • 用ChatGPT-5自然语言描述生成完整ERP模块
  • 工作记录 2017-03-24
  • ollama导入huggingface下载的大模型并量化
  • 11_常用函数
  • Golang的文件同步与备份
  • HLS入门之点灯大师
  • IPIP.NET-IP地理位置数据
  • 1. 购物车
  • Sentinel[超详细讲解]-7 -之 -熔断降级[异常比例阈值]
  • 万字重谈C++——类和对象篇
  • JAVA并发编程高级--深入解析 Java ReentrantLock:非公平锁与公平锁的实现原理
  • 【零基础入门unity游戏开发——2D篇】2D 游戏场景地形编辑器——TileMap的使用介绍
  • 虚拟电商-话费充值业务(六)话费充值业务回调补偿
  • MINIQMT学习课程Day3
  • Enovia许可配置和优化
  • seaweedfs分布式文件系统
  • RAC磁盘头损坏问题处理
  • 特征金字塔网络(FPN)详解
  • 【易订货-注册/登录安全分析报告】
  • Oracle触发器使用(二):伪记录和系统触发器
  • 构建个人专属知识库文件的RAG的大模型应用
  • BUUCTF-web刷题篇(9)
  • idea插件(自用)