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原创,创作不易,如果对您有帮助,欢迎小伙伴们点赞?、收藏⭐、留言?
目录一.加解密算法数字签名对称加密DES(DataEncryptionStandard)3DES(TripleDES)AES(AdvancedEncryptionStandard)RSA加密法DSA(DigitalSignatureAlgorithm)ECC(EllipticCurvesCryptography)非对称加密签名与加密过程非对称加密的应用对称加密与非对称加密的结合二.数字证书图解一.加解密算法加密简单而言就是通过一种算法将明文信息转换成密文信息,信息的的接收方能够通过密钥对密文信息进行解密获得明文信息的过程。根据加解密的密钥是否相同,算法可以分为对称加密、非对称加密、对称加密和非
给定一个数组a,什么是实现其组合直到第n的最佳方法?例如:a=%i[abc]n=2#Expected=>[[],[:a],[:b],[:c],[:a,b],[:b,:c],[:c,:a]] 最佳答案 做如下:a=%w[abc]n=30.upto(n).flat_map{|i|a.combination(i).to_a}#=>[[],["a"],["b"],["c"],["a","b"],#["a","c"],["b","c"],["a","b","c"]] 关于ruby-最多n的组合,我
我想合并多个事件记录关系例如,apple_companies=Company.where("namelike?","%apple%")banana_companies=Company.where("namelike?","%banana%")我想结合这两个关系。不是合并,合并是apple_companies.merge(banana_companies)=>Company.where("namelike?andnamelike?","%apple%","%banana%")我要Company.where("名字像?还是名字像?","%apple%","%banana%")之后,我会写代
我有一个任务列表(名称、starts_at),我试图在每日View中显示它们(就像iCal)。deftodays_tasks(day)Task.find(:all,:conditions=>["starts_atbetween?and?",day.beginning,day.ending]end我不知道如何将Time.now(例如“2009-04-1210:00:00”)动态转换为一天的开始(和结束),以便进行比较。 最佳答案 deftodays_tasks(now=Time.now)Task.find(:all,:conditio
我有一个熟悉的问题,看起来像是数学世界的排列/组合。如何通过ruby实现以下目标?badges="1-2-3"badge_cascade=[]badges.split("-").eachdo|b|badge_cascade["1","2","3"]ButIwantittobeis:=>["1","2","3","1-2","2-3","3-1","2-1","3-2","1-3","1-2-3","2-3-1","3-1-2"] 最佳答案 函数式方法:bs="1-2-3".split("-")strings=1.upto(bs.
在尝试解决“网格上的路径”问题时,我编写了代码defpaths(n,k)p=(1..n+k).to_ap.combination(n).to_a.sizeend代码工作正常,例如ifn==8andk==2代码返回45,这是正确的路径数。但是,当使用较大的数字时,代码非常慢,我正在努力想出如何加快这个过程。 最佳答案 与其构建组合数组只是为了计算它,不如编写function定义组合的数量。我敢肯定还有包含此功能和许多其他组合函数的gem。请注意,我使用的是gemDistribution对于Math.factorial方法,但这是另一种
对于一个电子商务应用程序,我试图将选项的散列(每个选项都有一系列选择)转换为代表这些选择组合的散列数组。例如:#Input:{:color=>["blue","grey"],:size=>["s","m","l"]}#Output:[{:color=>"blue",:size=>"s"},{:color=>"blue",:size=>"m"},{:color=>"blue",:size=>"m"},{:color=>"grey",:size=>"s"},{:color=>"grey",:size=>"m"},{:color=>"grey",:size=>"m"}]Input内部可能有额
1.问题描述使用Python的turtle(海龟绘图)模块提供的函数绘制直线。2.问题分析一幅复杂的图形通常都可以由点、直线、三角形、矩形、平行四边形、圆、椭圆和圆弧等基本图形组成。其中的三角形、矩形、平行四边形又可以由直线组成,而直线又是由两个点确定的。我们使用Python的turtle模块所提供的函数来绘制直线。在使用之前我们先介绍一下turtle模块的相关知识点。turtle模块提供面向对象和面向过程两种形式的海龟绘图基本组件。面向对象的接口类如下:1)TurtleScreen类:定义图形窗口作为绘图海龟的运动场。它的构造器需要一个tkinter.Canvas或ScrolledCanva
什么是0day漏洞?0day漏洞,是指已经被发现,但是还未被公开,同时官方还没有相关补丁的漏洞;通俗的讲,就是除了黑客,没人知道他的存在,其往往具有很大的突发性、破坏性、致命性。0day漏洞之所以称为0day,正是因为其补丁永远晚于攻击。所以攻击者利用0day漏洞攻击的成功率极高,往往可以达到目的并全身而退,而防守方却一无所知,只有在漏洞公布之后,才后知后觉,却为时已晚。“后知后觉、反应迟钝”就是当前安全防护面对0day攻击的真实写照!为了方便大家理解,中科三方为大家梳理当前安全防护模式下,一个漏洞从发现到解决的三个时间节点:T0:此时漏洞即0day漏洞,是已经被发现,还未被公开,官方还没有相
我一直在尝试用Ruby实现Luhn算法。我一直在执行以下步骤:该公式根据其包含的校验位验证数字,该校验位通常附加到部分帐号以生成完整帐号。此帐号必须通过以下测试:从最右边的校验位开始向左移动,每第二个数字的值加倍。将乘积的数字(例如,10=1+0=1、14=1+4=5)与原始数字的未加倍数字相加。如果总模10等于0(如果总和以零结尾),则根据Luhn公式该数字有效;否则无效。http://en.wikipedia.org/wiki/Luhn_algorithm这是我想出的:defvalidCreditCard(cardNumber)sum=0nums=cardNumber.to_s.s