蓝桥杯-特殊的多边形(dfs/前缀和)
假设一个 n边形 n 条边为 a1,a2,a3,⋯,an定义该 nn 边形的值 v=a1×a2×a3×⋯×an
定义两个 n边形不同是指至少有一条边的长度在一个 nn 边形中有使用而另一个 nn 边形没有用到,如 n 边形 (3,4,5,6) 和 (3,5,4,6) 是两个相同的 n 边形,(3,4,5,6) 和 (4,5,6,7) 是两个不相同的 n 边形。
现在有t 和 n,表示 t 个询问并且询问的是 n 边形,每个询问给定一个区间 [l,r],问有多少个 n 边形(要求该 n 边形自己的 n 条边的长度互不相同)的值在该区间范围内。
输入格式
第一行包含两个正整数 t、n,表示有 t 个询问,询问的是 n 边形。
接下来 t 行,每行有两个空格隔开的正整数 l、r,表示询问区间 [l,r]。
输出格式
输出共 t 行,第 i 行对应第 i 个查询的 n 边形个数。
样例输入
4 3
1 10
30 50
60 200
200 400
样例输出
0
1
18
32
整体思路
整体思路是先通过深度优先搜索(DFS)生成所有可能的 n 边形,统计每个边值出现的次数,再使用前缀和来快速计算每个询问区间内的 n 边形数量。
详细步骤
1. 深度优先搜索(DFS)生成所有可能的 n 边形
在代码中,dfs
函数用于生成所有可能的 n 边形。
- 参数说明:
last
:表示上一条边的长度,用于确保新边的长度大于上一条边,保证边的长度互不相同。sum
:表示当前已经选择的边的长度之和。cnt
:表示当前已经选择的边的数量。mul
:表示当前已经选择的边的长度的乘积。
- 终止条件:
- 当
cnt
等于 (n + 1) 时,说明已经选择了 n 条边,此时将该 n 边形的边值mul
对应的计数器pre[mul]
加 1。
- 当
- 递归过程:
- 从
last + 1
开始枚举新的边的长度 i。 - 如果 i *mul 大于 100000,则停止搜索,因为后续的乘积会更大,不会满足要求。
- 如果
cnt
等于 n 且sum
小于等于 i,则停止搜索,因为此时不满足多边形的条件(任意一条边的长度小于其余边的长度之和)。 - 递归调用
dfs
函数,更新last
为 i,sum
为sum + i
,cnt
为cnt + 1
,mul
为 i *mul。
- 从
2. 前缀和处理
在 DFS 完成后,使用前缀和数组 pre
来快速计算区间内的 n 边形数量。
- 对于前缀和数组
pre
,pre[i]
表示边值小于等于 i 的 n 边形的数量。 - 通过遍历 1 到 100000,将
pre[i]
更新为pre[i] + pre[i - 1]
。
3. 处理询问
对于每个询问的区间[l, r],通过前缀和数组计算边值在该区间内的 n 边形的数量。
- 边值在区间 [l, r] 内的 n 边形的数量为
pre[r] - pre[l - 1]
。
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
int t,n,l,r,pre[100005];
void dfs(int last,int sum,int cnt,int mul){
if(cnt==n+1){
pre[mul]++;
return ;
}
for(int i=last+1;i<=100000;++i){
if(i*mul>100000)return;
if(cnt==n&&sum<=i)return;
dfs(i,sum+i,cnt+1,i*mul);
}
}
signed main()
{
cin>>t>>n;
dfs(0,0,1,1);
for(int i=1;i<=1e5;i++){
pre[i]+=pre[i-1];
}
for(int i=0;i<t;++i){
cin>>l>>r;
cout<<pre[r]-pre[l-1]<<'\n';
}
return 0;
}