其他排列组合问题

目录

数组排列问题

力扣 634. 寻找数组的错位排列

力扣 629. K 个逆序对数组

力扣 1866. 恰有 K 根木棍可以看到的排列数目

力扣 2681. 英雄的力量

 单纯形排列问题

力扣 1621. 大小为 K 的不重叠线段的数目

力扣 1641. 统计字典序元音字符串的数目

子集(幂集)组合问题

力扣 77. 组合

力扣 78. 子集

力扣 90. 子集 II

三角形组合问题

(1)无规律集合

(2)连续的整数

(3)从1开始的连续的整数

CSU 1759 Triangle

杨辉三角

力扣 2221. 数组的三角和

阶乘数、组合数问题

CSU 2049 象棋

HDU 1220 Cube

力扣 1359. 有效的快递序列数目

力扣 2842. 统计一个字符串的 k 子序列美丽值最大的数目

其他组合问题

CSU 1134 Non-Decreasing Digits

 力扣 1735. 生成乘积数组的方案数

力扣 2963. 统计好分割方案的数目

力扣 LCP 25. 古董键盘


数组排列问题

力扣 634. 寻找数组的错位排列

在组合数学中,如果一个排列中所有元素都不在原先的位置上,那么这个排列就被称为 错位排列 。

给定一个从 1 到 n 升序排列的数组,返回 不同的错位排列 的数量 。由于答案可能非常大,你只需要将答案对 109+7 取余 输出即可。

示例 1:

输入: n = 3
输出: 2
解释: 原始的数组为 [1,2,3]。两个错位排列的数组为 [2,3,1] 和 [3,1,2]。
示例 2:

输入: n = 2
输出: 1
 

提示:

1 <= n <= 106

class Solution {
public:
	int findDerangement(int n) {
		long long ans = 0;
		for (int i = 2; i <= n; i++)ans = (ans*i + (i % 2 ? -1 : 1)) % 1000000007;
		return ans;
	}
};

力扣 629. K 个逆序对数组

逆序对的定义如下:对于数组 nums 的第 i 个和第 j 个元素,如果满足 0 <= i < j < nums.length 且 nums[i] > nums[j],则其为一个逆序对;否则不是。

给你两个整数 n 和 k,找出所有包含从 1 到 n 的数字,且恰好拥有 k 个 逆序对 的不同的数组的个数。由于答案可能很大,只需要返回对 109 + 7 取余的结果。

示例 1:

输入:n = 3, k = 0
输出:1
解释:
只有数组 [1,2,3] 包含了从1到3的整数并且正好拥有 0 个逆序对。

示例 2:

输入:n = 3, k = 1
输出:2
解释:
数组 [1,3,2] 和 [2,1,3] 都有 1 个逆序对。

提示:

  • 1 <= n <= 1000
  • 0 <= k <= 1000
class Solution {
public:
	long long kInversePairs(int n, int k) {
		if (k == 0)return 1;
		if (k < 0 || k>n*(n-1)/2)return 0;
		if (m[n][k])return m[n][k];
		return m[n][k] =(kInversePairs(n, k - 1) + kInversePairs(n - 1, k) - kInversePairs(n - 1, k - n) + 1000000007)%1000000007;
	}
	map<int, map<int, long long>>m;
};

力扣 1866. 恰有 K 根木棍可以看到的排列数目

有 n 根长度互不相同的木棍,长度为从 1 到 n 的整数。请你将这些木棍排成一排,并满足从左侧 可以看到 恰好 k 根木棍。从左侧 可以看到 木棍的前提是这个木棍的 左侧 不存在比它 更长的 木棍。

  • 例如,如果木棍排列为 [1,3,2,5,4] ,那么从左侧可以看到的就是长度分别为 13 、5 的木棍。

给你 n 和 k ,返回符合题目要求的排列 数目 。由于答案可能很大,请返回对 109 + 7 取余 的结果。

示例 1:

输入:n = 3, k = 2
输出:3
解释:[1,3,2], [2,3,1] 和 [2,1,3] 是仅有的能满足恰好 2 根木棍可以看到的排列。
可以看到的木棍已经用粗体+斜体标识。

示例 2:

输入:n = 5, k = 5
输出:1
解释:[1,2,3,4,5] 是唯一一种能满足全部 5 根木棍可以看到的排列。
可以看到的木棍已经用粗体+斜体标识。

示例 3:

输入:n = 20, k = 11
输出:647427950
解释:总共有 647427950 (mod 109 + 7) 种能满足恰好有 11 根木棍可以看到的排列。

提示:

  • 1 <= n <= 1000
  • 1 <= k <= n

思路:

枚举最小数的位置,把问题化成子问题,得到递推式。

我实现的是二维DP的空间压缩。

class Solution {
public:
	int rearrangeSticks(int n, int k) {
		vector<long long>ans(k + 1,0);
		ans[1] = 1;
		for (int i = 2; i <= n; i++) {
			for (int j = min(i, k); j; j--) {
				ans[j] = (ans[j] * (i-1) + ans[j - 1]) % 1000000007;
			}
		}
		return ans[k];
	}
};

力扣 2681. 英雄的力量

给你一个下标从 0 开始的整数数组 nums ,它表示英雄的能力值。如果我们选出一部分英雄,这组英雄的 力量 定义为:

  • i0 ,i1 ,... ik 表示这组英雄在数组中的下标。那么这组英雄的力量为 max(nums[i0],nums[i1] ... nums[ik])2 * min(nums[i0],nums[i1] ... nums[ik]) 。

请你返回所有可能的 非空 英雄组的 力量 之和。由于答案可能非常大,请你将结果对 109 + 7 取余。

 

示例 1:

输入:nums = [2,1,4]
输出:141
解释:
第 1 组:[2] 的力量为 22 * 2 = 8 。
第 2 组:[1] 的力量为 12 * 1 = 1 。
第 3 组:[4] 的力量为 42 * 4 = 64 。
第 4 组:[2,1] 的力量为 22 * 1 = 4 。
第 5 组:[2,4] 的力量为 42 * 2 = 32 。
第 6 组:[1,4] 的力量为 42 * 1 = 16 。
第​ ​​​​​​7 组:[2,1,4] 的力量为 42 * 1 = 16 。
所有英雄组的力量之和为 8 + 1 + 64 + 4 + 32 + 16 + 16 = 141 。

示例 2:

输入:nums = [1,1,1]
输出:7
解释:总共有 7 个英雄组,每一组的力量都是 1 。所以所有英雄组的力量之和为 7 。

 

提示:

  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 109
class Solution {
public:
    int sumOfPower(vector<int>& nums) {
        int p = 1000000007;
        sort(nums.begin(),nums.end());
        vector<int>s=nums;
        s.insert(s.begin(),0);
        s.erase(s.end()-1);
        long long ans=0;
        for(int i=0;i<s.size();i++){
            if(i)s[i]=(s[i]+s[i-1]*2%p)%p;
            ans+=((long long)(s[i]+nums[i]))*nums[i]%p*nums[i]%p;
        }
        return ans%p;
    }
};

 单纯形排列问题

n个互相区分的非负整数,和为s,有多少种情况?

相当于s个物品和n-1个隔板任意顺序排成一列,答案是C(s+n-1,s)

力扣 1621. 大小为 K 的不重叠线段的数目

给你一维空间的 n 个点,其中第 i 个点(编号从 0 到 n-1)位于 x = i 处,请你找到 恰好 k 个不重叠 线段且每个线段至少覆盖两个点的方案数。线段的两个端点必须都是 整数坐标 。这 k 个线段不需要全部覆盖全部 n 个点,且它们的端点 可以 重合。

请你返回 k 个不重叠线段的方案数。由于答案可能很大,请将结果对 109 + 7 取余 后返回。

示例 1:

输入:n = 4, k = 2
输出:5
解释:
如图所示,两个线段分别用红色和蓝色标出。
上图展示了 5 种不同的方案 {(0,2),(2,3)},{(0,1),(1,3)},{(0,1),(2,3)},{(1,2),(2,3)},{(0,1),(1,2)} 。

示例 2:

输入:n = 3, k = 1
输出:3
解释:总共有 3 种不同的方案 {(0,1)}, {(0,2)}, {(1,2)} 。

示例 3:

输入:n = 30, k = 7
输出:796297179
解释:画 7 条线段的总方案数为 3796297200 种。将这个数对 109 + 7 取余得到 796297179 。

示例 4:

输入:n = 5, k = 3
输出:7

示例 5:

输入:n = 3, k = 2
输出:1

提示:

  • 2 <= n <= 1000
  • 1 <= k <= n-1

思路:
k个正整数和k+1个非负数的和为n-1,即2k+1个非负数的和为n-k-1

class Solution {
public:
	int numberOfSets(int n, int k) {
		return ComNum::getValue(n+k-1,k*2, 1000000007) % 1000000007;
	}
};

力扣 1641. 统计字典序元音字符串的数目

给你一个整数 n,请返回长度为 n 、仅由元音 (aeiou) 组成且按 字典序排列 的字符串数量。

字符串 s 按 字典序排列 需要满足:对于所有有效的 is[i] 在字母表中的位置总是与 s[i+1] 相同或在 s[i+1] 之前。

示例 1:

输入:n = 1
输出:5
解释:仅由元音组成的 5 个字典序字符串为 ["a","e","i","o","u"]

示例 2:

输入:n = 2
输出:15
解释:仅由元音组成的 15 个字典序字符串为
["aa","ae","ai","ao","au","ee","ei","eo","eu","ii","io","iu","oo","ou","uu"]
注意,"ea" 不是符合题意的字符串,因为 'e' 在字母表中的位置比 'a' 靠后

示例 3:

输入:n = 33
输出:66045

提示:

  • 1 <= n <= 50 
class Solution {
public:
    int countVowelStrings(int n) {
        return (n+4)*(n+3)*(n+2)*(n+1)/24;
    }
};

子集(幂集)组合问题

力扣 77. 组合

给定两个整数 n 和 k,返回范围 [1, n] 中所有可能的 k 个数的组合。

你可以按 任何顺序 返回答案。

示例 1:

输入:n = 4, k = 2
输出:
[
  [2,4],
  [3,4],
  [2,3],
  [1,2],
  [1,3],
  [1,4],
]

示例 2:

输入:n = 1, k = 1
输出:[[1]]

提示:

  • 1 <= n <= 20
  • 1 <= k <= n
class Solution {
public:
	vector<vector<int>> combine(int n, int k) {
		vector<int>v;
		for (int i = 1; i <= n; i++)v.push_back(i);
		return GetAllCombina(v, k, true);
	}
};

力扣 78. 子集

给你一个整数数组 nums ,数组中的元素 互不相同 。返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。你可以按 任意顺序 返回解集。

示例 1:

输入:nums = [1,2,3]
输出:[[],[1],[2],[1,2],[3],[1,3],[2,3],[1,2,3]]

示例 2:

输入:nums = [0]
输出:[[],[0]]

提示:

  • 1 <= nums.length <= 10
  • -10 <= nums[i] <= 10
  • nums 中的所有元素 互不相同
#define GetAllSubsets SetCombina::getAllSubsets //求所有子集,flag表示是否去掉重复子集
class Solution {
public:
	vector<vector<int>> subsets(vector<int>& nums) {
		return GetAllSubsets(nums,false);
	}
};

力扣 90. 子集 II

给你一个整数数组 nums ,其中可能包含重复元素,请你返回该数组所有可能的子集(幂集)。

解集 不能 包含重复的子集。返回的解集中,子集可以按 任意顺序 排列。

示例 1:

输入:nums = [1,2,2]
输出:[[],[1],[1,2],[1,2,2],[2],[2,2]]

示例 2:

输入:nums = [0]
输出:[[],[0]]

提示:

  • 1 <= nums.length <= 10
  • -10 <= nums[i] <= 10
#define GetAllSubsets SetCombina::getAllSubsets //求所有子集,flag表示是否去掉重复子集
class Solution {
public:
	vector<vector<int>> subsetsWithDup(vector<int>& nums) {
		return GetAllSubsets(nums,true);
	}
};

三角形组合问题

求在一个有限集合中,选取3个不同的正数,能构成三角形的方案数。

(1)无规律集合

先排序,再用双指针,时间复杂度O(n^2)

(2)连续的整数

假设集合就是对应[a,b]闭区间,2<=a<=b-2,那么方案数就是

f(a)+f(a+1)+...+f(b-2),其中f(x)=\left\{\begin{matrix} (b-x-1)(b-x)/2,b<=2x\\ (x-1)(2b-3x)/2,else \end{matrix}\right.

int f(int b, int x)
{
	if (b <= x * 2)return (b - x - 1)*(b - x) / 2;
	return (x - 1)*(b * 2 - x * 3) / 2;
}
int fs(int a, int b)
{
	int s = 0;
	for (int i = a; i <= b - 2; i++)s += f(b, i);
	return s;
}

(3)从1开始的连续的整数

求f(2)+...+f(n-2),其中f(x)=\left\{\begin{matrix} (n-x-1)(n-x)/2,n<=2x\\ (x-1)(2n-3x)/2,else \end{matrix}\right.

化简起来有点费劲,但是结果很简单:

((n - 1)*(n - 2)*(n - 3) / 6 + (n - 1) / 2 * (n / 2 - 1)) / 2,这里都是整数除法

CSU 1759 Triangle

Description
给你长度为1~n n条边,请你求出有多少种组合方法数可以选出三条边构成三角形

Input
多组数据输入输出(数据组数考虑为最大可能性)
每组数据输入一个正整数n,表示有n条长度的边可供选择(n<=10000)

Output
每组数据输出可构成三角形的边的选择方法数

Sample Input
2
4
Sample Output
0
1

思路:

#include<iostream>
using namespace std;
 
int main()
{
	long long n;
	while (cin >> n)cout << ((n - 1)*(n - 2)*(n - 3) / 6 + (n - 1) / 2 * (n / 2 - 1)) / 2 << endl;
	return 0;
}

杨辉三角

力扣 2221. 数组的三角和

给你一个下标从 0 开始的整数数组 nums ,其中 nums[i] 是 0 到 9 之间(两者都包含)的一个数字。

nums 的 三角和 是执行以下操作以后最后剩下元素的值:

  1. nums 初始包含 n 个元素。如果 n == 1 ,终止 操作。否则,创建 一个新的下标从 0 开始的长度为 n - 1 的整数数组 newNums 。
  2. 对于满足 0 <= i < n - 1 的下标 i ,newNums[i] 赋值 为 (nums[i] + nums[i+1]) % 10 ,% 表示取余运算。
  3. 将 newNums 替换 数组 nums 。
  4. 从步骤 1 开始 重复 整个过程。

请你返回 nums 的三角和。

示例 1:

输入:nums = [1,2,3,4,5]
输出:8
解释:
上图展示了得到数组三角和的过程。

示例 2:

输入:nums = [5]
输出:5
解释:
由于 nums 中只有一个元素,数组的三角和为这个元素自己。

提示:

  • 1 <= nums.length <= 1000
  • 0 <= nums[i] <= 9
class Solution {
public:
	int triangularSum(vector<int>& nums) {
		int n = nums.size() - 1;
		auto v = ComNum<10>::getAllValue(n, n, 10);
		int ans = 0;
		for (int i = 0; i <= n; i++)ans += v[n][i] * nums[i];
		return ans % 10;
	}
};

阶乘数、组合数问题

有很多五花八门的排列组合问题,稍微一分析,就能得到答案是一个关于阶乘数、组合数的解析式。

CSU 2049 象棋

题目:

Description
車是中国象棋中的一种棋子,它能攻击同一行或同一列中没有其他棋子阻隔的棋子。一天,小度在棋盘上摆起了许多車……他想知道,在一共N×M个点的矩形棋盘中摆最多个数的車使其互不攻击的方案数。他经过思考,得出了答案。但他仍不满足,想增加一个条件:对于任何一个車A,如果有其他一个車B在它的上方(車B行号小于車A),那么車A必须在車B的右边(車A列号大于車B)。
现在要问问你,满足要求的方案数是多少 。

Input
第一行一个正整数T,表示数据组数。( T<=10)
对于每组数据:一行,两个正整数N和M(N<=100000,M<=100000)。

Output
对于每组数据输出一行,代表方案数模1000000007(10^9+7)。

Sample Input
1
1 1
Sample Output
1


就是简单的计算组合数

代码:

#include<iostream>
using namespace std;
 
const int m = 100000;
int list[m], p[9592];//9592个素数
void getp()//在p数组中存所有不超过m的素数
{
	p[0] = 2;
	int key = 0;
	for (int i = 0; i < m; i++)list[i] = i % 2;
	for (int i = 3; i < m; i += 2)if (list[i])
	{
		p[++key] = i;
		for (int j = i + i; j < m; j += i)list[j] = 0;
	}
}
 
int degree(int m, int p)//求m!中素数p的次数
{
	if (m)return degree(m / p, p) + m / p;
	return 0;
}
 
int main()
{
	getp();
	int t, n, m, mi;
	cin >> t;
	while (t--)
	{
		cin >> n >> m;
		if (n < m)n ^= m ^= n ^= m;
		long long ans = 1;
		for (int i = 0; i < 9592; i++)
		{
			mi = degree(n, p[i]) - degree(m, p[i]) - degree(n - m, p[i]);
			while (mi--)ans = ans * p[i] % 1000000007;
		}
		cout << ans << endl;
	}
	return 0;
}

HDU 1220 Cube

题目:
Description

Cowl is good at solving math problems. One day a friend asked him such a question: You are given a cube whose edge length is N, it is cut by the planes that was paralleled to its side planes into N * N * N unit cubes. Two unit cubes may have no common points or two common points or four common points. Your job is to calculate how many pairs of unit cubes that have no more than two common points. 

Process to the end of file. 
Input

There will be many test cases. Each test case will only give the edge length N of a cube in one line. N is a positive integer(1<=N<=30). 
Output

For each test case, you should output the number of pairs that was described above in one line. 
Sample Input

1
2
3
Sample Output

0
16
297

这个很容易推导,只要用所有的二元组C(n^3,2),去掉不满足条件的3*n*n*(n-1)即可得到答案

代码:

#include<iostream>
using namespace std;

int main()
{
	int n, a;
	while (cin >> n)
	{
		a = n*n*(n + 1) + n - 6;
		cout << n*n*(n - 1)*a / 2 << endl;
	}
	return 0;
}

力扣 1359. 有效的快递序列数目

给你 n 笔订单,每笔订单都需要快递服务。

计算所有有效的 取货 / 交付 可能的顺序,使 delivery(i) 总是在 pickup(i) 之后。

由于答案可能很大,请返回答案对 10^9 + 7 取余的结果。

示例 1:

输入:n = 1
输出:1
解释:只有一种序列 (P1, D1),物品 1 的配送服务(D1)在物品 1 的收件服务(P1)后。

示例 2:

输入:n = 2
输出:6
解释:所有可能的序列包括:
(P1,P2,D1,D2),(P1,P2,D2,D1),(P1,D1,P2,D2),(P2,P1,D1,D2),(P2,P1,D2,D1) 和 (P2,D2,P1,D1)。
(P1,D2,P2,D1) 是一个无效的序列,因为物品 2 的收件服务(P2)不应在物品 2 的配送服务(D2)之后。

示例 3:

输入:n = 3
输出:90

提示:

  • 1 <= n <= 500

思路:

C(2n,2) * C(2n-2,2) * ... * C(2,2)  =  (2n)! / 2^n

class Solution {
public:
    int countOrders(int n) {
        long long ans=1,flag=n;
        for(int i=1;i<=n*2;i++){
            int a=i;
            while(a%2==0 && flag)a/=2,flag--;
            ans=ans*a%1000000007;
        }
        return ans;
    }
};

力扣 2842. 统计一个字符串的 k 子序列美丽值最大的数目

给你一个字符串 s 和一个整数 k 。

k 子序列指的是 s 的一个长度为 k 的 子序列 ,且所有字符都是 唯一 的,也就是说每个字符在子序列里只出现过一次。

定义 f(c) 为字符 c 在 s 中出现的次数。

k 子序列的 美丽值 定义为这个子序列中每一个字符 c 的 f(c) 之  。

比方说,s = "abbbdd" 和 k = 2 ,我们有:

  • f('a') = 1f('b') = 3f('d') = 2
  • s 的部分 k 子序列为:
    • "abbbdd" -> "ab" ,美丽值为 f('a') + f('b') = 4
    • "abbbdd" -> "ad" ,美丽值为 f('a') + f('d') = 3
    • "abbbdd" -> "bd" ,美丽值为 f('b') + f('d') = 5

请你返回一个整数,表示所有 k 子序列 里面 美丽值 是 最大值 的子序列数目。由于答案可能很大,将结果对 109 + 7 取余后返回。

一个字符串的子序列指的是从原字符串里面删除一些字符(也可能一个字符也不删除),不改变剩下字符顺序连接得到的新字符串。

注意:

  • f(c) 指的是字符 c 在字符串 s 的出现次数,不是在 k 子序列里的出现次数。
  • 两个 k 子序列如果有任何一个字符在原字符串中的下标不同,则它们是两个不同的子序列。所以两个不同的 k 子序列可能产生相同的字符串。

示例 1:

输入:s = "bcca", k = 2
输出:4
解释:s 中我们有 f('a') = 1 ,f('b') = 1 和 f('c') = 2 。
s 的 k 子序列为:
bcca ,美丽值为 f('b') + f('c') = 3
bcca ,美丽值为 f('b') + f('c') = 3
bcca ,美丽值为 f('b') + f('a') = 2
bcca ,美丽值为 f('c') + f('a') = 3
bcca ,美丽值为 f('c') + f('a') = 3
总共有 4 个 k 子序列美丽值为最大值 3 。
所以答案为 4 。

示例 2:

输入:s = "abbcd", k = 4
输出:2
解释:s 中我们有 f('a') = 1 ,f('b') = 2 ,f('c') = 1 和 f('d') = 1 。
s 的 k 子序列为:
abbcd ,美丽值为 f('a') + f('b') + f('c') + f('d') = 5
abbcd ,美丽值为 f('a') + f('b') + f('c') + f('d') = 5 
总共有 2 个 k 子序列美丽值为最大值 5 。
所以答案为 2 。

提示:

  • 1 <= s.length <= 2 * 105
  • 1 <= k <= s.length
  • s 只包含小写英文字母。
class Solution {
public:
	int countKSubsequencesWithMaxBeauty(string s, int k) {
		map<char, int>m;
		for (auto c : s)m[c]++;
		map<int, int>m2;
		for (auto mi : m)m2[mi.second] ++;
		auto it = m2.rbegin();
		long long ans = 1;
		while (k > it->second) {
			k -= it->second;
			for (int i = 0; i < it->second; i++)ans = (ans*it->first) % p;
			if(++it == m2.rend())return 0;
		}
		ans = (ans * opt.getComNum(it->second, k, p)) % p;
		while(k--)ans = (ans*it->first) % p;
		return ans;
	}
	ComNum<> opt;
	int p = 1000000007;
};

其他组合问题

CSU 1134 Non-Decreasing Digits

题目:

Description
A number is said to be made up of non-decreasing digits if all the digits to the left of any digit is less 
than or equal to that digit.  For example, the four-digit number 1234 is composed of digits that are 
non-decreasing.  Some other four-digit numbers that are composed of non-decreasing digits are 
0011, 1111, 1112, 1122, 2223.  As it turns out, there are exactly 715 four-digit numbers composed of 
non-decreasing digits. 
 
Notice that leading zeroes are required: 0000, 0001, 0002 are all valid four-digit numbers with non-
decreasing digits. 
 
For this problem, you will write a program that determines how many such numbers there are with a 
specified number of digits.

Input
The first line of input contains a single integer P, (1 £ P £ 1000), which is the number of data sets that 
follow.  Each data set is a single line that contains the data set number, followed by a space, followed 
by a decimal integer giving the number of digits N, (1 £ N £ 64). 

Output
For each data set there is one line of output.  It contains the data set number followed by a single 
space, followed by the number of N digit values that are composed entirely of non-decreasing digits.

Sample Input
3
1 2
2 3
3 4
Sample Output
1 55
2 220
3 715

排列组合

ans=C(n+9,9)

代码:
 

#include<iostream>
using namespace std;
 
int main()
{
	int n;
	cin >> n;
	while (cin >> n)
	{
		cout << n << " ";
		cin >> n;
		long long ans = 1;
		for (int i = 1; i <= 9; i++)ans *= n + i;
		for (int i = 1; i <= 9; i++)ans /= i;
		cout << ans << endl;
	}
	return 0;
}

 力扣 1735. 生成乘积数组的方案数

给你一个二维整数数组 queries ,其中 queries[i] = [ni, ki] 。第 i 个查询 queries[i] 要求构造长度为 ni 、每个元素都是正整数的数组,且满足所有元素的乘积为 ki ,请你找出有多少种可行的方案。由于答案可能会很大,方案数需要对 109 + 7 取余 。

请你返回一个整数数组 answer,满足 answer.length == queries.length ,其中 answer[i]是第 i 个查询的结果。

示例 1:

输入:queries = [[2,6],[5,1],[73,660]]
输出:[4,1,50734910]
解释:每个查询之间彼此独立。
[2,6]:总共有 4 种方案得到长度为 2 且乘积为 6 的数组:[1,6],[2,3],[3,2],[6,1]。
[5,1]:总共有 1 种方案得到长度为 5 且乘积为 1 的数组:[1,1,1,1,1]。
[73,660]:总共有 1050734917 种方案得到长度为 73 且乘积为 660 的数组。1050734917 对 109 + 7 取余得到 50734910 。

示例 2 :

输入:queries = [[1,1],[2,2],[3,3],[4,4],[5,5]]
输出:[1,2,3,10,5]

提示:

  • 1 <= queries.length <= 104
  • 1 <= ni, ki <= 104
class Solution {
public:
	vector<int> waysToFillArray(vector<vector<int>>& queries) {
		int p = 1000000007;
		vector<int>ret;
		ret.reserve(queries.size());
		for (auto v : queries) {
			vector<pair<int, int>> ps = Fenjie(v[1]);
			long long ans = 1;
			for (auto par : ps)ans = ans * ComNum::getValue(v[0] - 1 + par.second, par.second, p) % p;
			ret.push_back(ans);
		}
		return ret;
	}
};

力扣 2963. 统计好分割方案的数目

给你一个下标从 0 开始、由 正整数 组成的数组 nums

将数组分割成一个或多个 连续 子数组,如果不存在包含了相同数字的两个子数组,则认为是一种 好分割方案 。

返回 nums 的 好分割方案 的 数目

由于答案可能很大,请返回答案对 109 + 7 取余 的结果。

示例 1:

输入:nums = [1,2,3,4]
输出:8
解释:有 8 种 好分割方案 :([1], [2], [3], [4]), ([1], [2], [3,4]), ([1], [2,3], [4]), ([1], [2,3,4]), ([1,2], [3], [4]), ([1,2], [3,4]), ([1,2,3], [4]) 和 ([1,2,3,4]) 。

示例 2:

输入:nums = [1,1,1,1]
输出:1
解释:唯一的 好分割方案 是:([1,1,1,1]) 。

示例 3:

输入:nums = [1,2,1,3]
输出:2
解释:有 2 种 好分割方案 :([1,2,1], [3]) 和 ([1,2,1,3]) 。

提示:

  • 1 <= nums.length <= 105
  • 1 <= nums[i] <= 109
class Solution {
public:
	int numberOfGoodPartitions(vector<int>& nums) {
		map<int, int>m;
		for (int i = 0; i < nums.size(); i++)m[nums[i]] = i;
		int id = 0, ans = 500000004;
		while (id < nums.size()) {
			int maxId = id, id2 = id;
			while (id2 <= maxId)maxId = max(maxId, m[nums[id2++]]);
			id = id2;
			ans = (ans * 2) % 1000000007;
		}
		return ans;
	}
};

力扣 LCP 25. 古董键盘

小扣在秋日市集购买了一个古董键盘。由于古董键盘年久失修,键盘上只有 26 个字母 a~z 可以按下,且每个字母最多仅能被按 k 次。

小扣随机按了 n 次按键,请返回小扣总共有可能按出多少种内容。由于数字较大,最终答案需要对 1000000007 (1e9 + 7) 取模。

示例 1:

输入:k = 1, n = 1

输出:26

解释:由于只能按一次按键,所有可能的字符串为 "a", "b", ... "z"

示例 2:

输入:k = 1, n = 2

输出:650

解释:由于只能按两次按键,且每个键最多只能按一次,所有可能的字符串(按字典序排序)为 "ab", "ac", ... "zy"

提示:

  • 1 <= k <= 5
  • 1 <= n <= 26*k

思路:枚举第一个字母的数量,化成二维DP问题。

class Solution {
public:
	int keyboard(int k, int n) {
		this->k = k;
		v = GetAllComValue(n, k);
		return dp(26, n);
	}
	long long dp(int t, int n) {
		if (n > k*t)return 0;
		if (t == 1 || n == 0)return 1;
		if (m[t][n])return m[t][n];
		long long s = 0;
		for (int i = min(n, k); i >= 0; i--) {
			s += dp(t - 1, n - i)*v[n][i];
		}
		return m[t][n] = s % 1000000007;
	}
	map<int, map<int, int>>m;
	int k;
	vector<vector<int>>v;
};

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值