【ACM算法竞赛日常训练】DAY4题解与分析【树】【子序列】| 组合数学 | 动态规划

yizhihongxing

DAY4共2题:

  • 树(组合数学)

  • 子序列(dp,数学)

? 作者:Eriktse
? 简介:19岁,211计算机在读,现役ACM银牌选手?力争以通俗易懂的方式讲解算法!❤️欢迎关注我,一起交流C++/Python算法。(优质好文持续更新中……)?
? 原文链接(阅读原文获得更好阅读体验):https://www.eriktse.com/algorithm/1095.html

题目传送门:https://ac.nowcoder.com/acm/problem/13611

通过观察条件“一个染色方案是合法的,当且仅当对于所有相同颜色的点对(x,y),x到y的路径上的所有点的颜色都要与x和y相同。”我们可以发现,当且仅当染色的点可以全部连通时可以满足条件。

所以现在问题是如何将n个点划分为k块。

我们可以发现在树上,任意删除一条边都会使得联通块个数 + 1

其实块数只要<= k即可,因为我们可以有一些颜色不使用。所以要划分为i块,只需要从n - 1条边中任选i - 1条进行删除即可,方案数是C(n - 1, i - 1)

假设现在我们得到了i (i <= k)个联通块,需要将i种颜色染上去,首先需要C(k, i)种方法取出颜色,然后A(i, i)一个全排列将颜色染上去。

所以答案公式如下:

\[ans=\sum_{i=1}^{k}C(n - 1, i - 1)C(k, i)i!
\]

可能涉及一些快速幂乘法逆元的知识,需要自行学习。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 350, p = 1e9 + 7;

int fac[maxn];

int qmi(int a, int b)
{
    int res = 1;
    while(b)
    {
        if(b & 1)res = res * a % p;
        a = a * a % p, b >>= 1;
    }
    return res;
}

int inv(int x){return qmi(x, p - 2);}

int C(int n, int m)
{
    if(n < m || n < 0 || m < 0)return 0;
    return fac[n] * inv(fac[n - m] * fac[m] % p) % p;
}

signed main()
{
    int n, k;scanf("%lld %lld", &n, &k);
    fac[0] = 1;
    for(int i = 1;i <= n; ++ i)fac[i] = fac[i - 1] * i % p;
    
    int ans = 0;
    for(int i = 1;i <= n; ++ i)//分为i块
    {
        int tmp = C(n - 1, i - 1) * C(k, i) % p * fac[i] % p;
        ans = (ans + tmp) % p;
    }
    printf("%lld\n", ans);
    return 0;
}

子序列

题目传送门:https://ac.nowcoder.com/acm/problem/17065

小技巧:观察数据范围,比较小,应该可以容纳O(n^3)的复杂度,所以可以大胆考虑dp。

首先定义状态dp[i][j]表示以第i个元素结尾,且长度为j的序列的个数

再考虑一下转移,题目中的条件可以进行一些转换:

\[{a_{p_i}}^{p_j} < {a_{p_j}}^{p_i}
\]

等价于:

\[\frac{log(a_{p_i})}{p_i} < \frac{log(a_{p_j})}{p_j}
\]

我们可以记:

\[b_i = \frac{log(a_{p_i})}{p_i}
\]

也就是说对于选出的子序列中的每一个元素,他们满足一个偏序关系,只要我的b[j] > b[i],那么b[j]将会大于所有的b[k] (k < i)

所以我们可以考虑以下的转移:

\[dp_{i, j} = \sum_{k=1}^{i - 1}[b_i > b_k] \times dp_{k, j - 1}
\]

考虑初始化,当最后一个元素确定,序列长度为1(j = 1)时,方案仅有1种。

最后的答案是将所有情况加起来(注意取模,不过这道题数据较弱,不取模也可以过)。

#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxn = 109, p = 1e9 + 7;

//dp[i][j]表示以第i个元素结尾,长度为j的方案数
int a[maxn], dp[maxn][maxn];


signed main()
{
	int n;scanf("%lld", &n);
	for(int i = 1;i <= n; ++ i)scanf("%lld", a + i);
	
	for(int i = 1;i <= n; ++ i)
    {
        dp[i][1] = 1;
        for(int j = 1;j <= i; ++ j)
        {
            for(int k = 1; k < i; ++ k)
            {
                if(log(a[k]) / k < log(a[i]) / i)
                {
                    dp[i][j] += dp[k][j - 1];
                    dp[i][j] %= p;
                }
            }
        }
    }

	int ans = 0;
	for(int i = 1;i <= n; ++ i)
		for(int j = 1;j <= i; ++ j)
        {
			ans = (ans + dp[i][j]) % p;
        }
	printf("%lld\n", ans);
	return 0;
}

? 本文由eriktse原创,创作不易,如果对您有帮助,欢迎小伙伴们点赞?、收藏⭐、留言?

原文链接:https://www.cnblogs.com/eriktse/p/17262568.html

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:【ACM算法竞赛日常训练】DAY4题解与分析【树】【子序列】| 组合数学 | 动态规划 - Python技术站

(0)
上一篇 2023年4月18日
下一篇 2023年4月18日

相关文章

  • C#数据结构之顺序表(SeqList)实例详解

    C#数据结构之顺序表(SeqList)实例详解 顺序表(SeqList)概述 顺序表(SeqList)是一种线性表存储结构,它的特点是元素的存储位置是连续的。因为它的存储结构是数组,所以在访问和修改元素时,可以通过数组下标进行快速定位。顺序表在内存中的存储相对紧凑,因此查找和修改效率都很高,适用于大多数元素较少、但是需要频繁访问的场景。 实现顺序表(SeqL…

    数据结构 2023年5月17日
    00
  • 详解弗洛伊德算法原理与使用方法

    弗洛伊德算法 弗洛伊德算法,也称为Floyd-Warshall算法,是一种用于解决有权图中所有顶点之间最短路径问题的动态规划算法。该算法时间复杂度为O(n^3),其中n为图中顶点数。 算法作用 弗洛伊德算法可以用于计算有向图或无向图中的所有节点对之间的最短路径,同时还能够处理负权边的情况。 算法实现 该算法使用一个n * n的矩阵dist来保存任意两个顶点之…

    算法 2023年3月27日
    00
  • C++ 数据结构超详细讲解单链表

    C++ 数据结构超详细讲解单链表 什么是单链表 单链表是一种常见的线性数据结构,它由若干个节点组成,每个节点包含两部分内容:数据域和指针域。其中数据域存储节点所携带的数据,而指针域存储下一个节点的地址。 单链表的性质在于每个节点只有一个指针域,而第一个节点叫做头节点,通常不存放数据,只用来标注链表的起始位置。最后一个节点的指针域指向 NULL,即表示链表的结…

    数据结构 2023年5月17日
    00
  • Java数据结构之对象的比较

    Java数据结构之对象的比较 在Java中,对象的比较是非常重要的操作。我们常常需要对不同的对象进行比较,以便对它们进行排序、按照某个条件过滤等操作。本文将详细讲解Java中对象的比较,并给出一些示例来说明。 对象的比较方法 Java中有两种对象比较方法:值比较和引用比较。值比较就是比较两个对象的值是否相等,而引用比较是比较两个对象是否是同一个对象。 值比较…

    数据结构 2023年5月17日
    00
  • 剑指 Offer 33. 二叉搜索树的后序遍历序列(java解题)

    目录 1. 题目 2. 解题思路 3. 数据类型功能函数总结 4. java代码 5. 踩坑小记 递归调用,显示StackOverflowError 1. 题目 输入一个整数数组,判断该数组是不是某二叉搜索树的后序遍历结果。如果是则返回 true,否则返回 false。假设输入的数组的任意两个数字都互不相同。 参考以下这颗二叉搜索树: 5 / \ 2 6 /…

    算法与数据结构 2023年4月23日
    00
  • javascript数据结构与算法之检索算法

    JavaScript 数据结构与算法之检索算法 什么是检索算法 检索算法,也称为查找算法,是解决在数据集合中寻找某个特定元素的算法。 比如,在一个给定的数组中查找特定的元素,或者在一个字典中查找某个特定单词的定义等等,这些都是检索算法的应用场景。 JavaScript 中的检索算法主要有以下几种:线性查找、二分查找、哈希查找。 线性查找 线性查找,也叫顺序查…

    数据结构 2023年5月17日
    00
  • python八大排序算法速度实例对比

    Python八大排序算法速度实例对比 排序算法是计算机科学中的基本问题之一,它的目的是将一组数据按照定的顺序排列。在Python中,可以使用多种排序算法来对数据进行。本文将介绍Python的八大排序算法,并对它们的速度进行实例对比。 八大排序算法 1. 冒泡排序 冒泡排序是一种简单的排序算法,它的基本思想是通过断交换相邻的元素,将较大的元素逐渐“冒泡”到数组…

    python 2023年5月13日
    00
  • Redis高效率原因及数据结构分析

    Redis高效率原因及数据结构分析 Redis高效率的原因 Redis是一款高性能、高可靠性的内存数据库,其高效率的原因主要体现在以下几个方面: 1. 内存存储 Redis数据完全存储在内存中,而不是像传统的关系型数据库一样存储在磁盘中。内存的读写速度要远远快于磁盘的读写速度,因此Redis在数据读写时的速度非常快,能够达到每秒钟数百万次的读写操作。 2. …

    数据结构 2023年5月17日
    00
合作推广
合作推广
分享本页
返回顶部