每日一签
题目
给定一个
1
1
1~
N
N
N的排列
a
[
i
]
a[i]
a[i],每次将相邻两个数相加,得到新序列,再对新序列重复这样的操作,显然每次得到的序列都比上一次的序列长度少
1
1
1,最终只剩一个数字。
例如:
3 1 2 4
4 3 6
7 9
16
现在如果知道
N
N
N和最后得到的数字
s
u
m
sum
sum,请求出最初序列
a
[
i
]
a[i]
a[i],为
1
1
1~
N
N
N的一个排列。若有多种答案,则输出字典序最小的那一个。数据保证有解。
输入样例:
4 16
输出样例:
3 1 2 4
思路
见下图:
从下往上看,我们可以发现最终得到的值为
s
u
m
=
1
+
2
×
4
+
3
×
6
+
4
×
4
+
5
sum = 1+2\times 4+3\times 6+4\times 4+5
sum=1+2×4+3×6+4×4+5
由此规律可得如下公式:
S
u
m
(
n
)
=
a
[
1
]
×
C
n
−
1
0
+
a
[
2
]
×
C
n
−
1
1
+
…
…
+
a
[
n
]
×
C
n
−
1
n
−
1
Sum(n) = a[1]\times C_{n-1}^{0}+a[2]\times C_{n-1}^{1}+……+a[n]\times C_{n-1}^{n-1}
Sum(n)=a[1]×Cn−10+a[2]×Cn−11+……+a[n]×Cn−1n−1
那这就简单了啊,利用库函数next_permutation
实现全排列,第一次满足由上述公式所得结果为输入的
s
u
m
sum
sum值得就是我们需要得答案。
代码
#include<bits/stdc++.h>
#include<algorithm>
#include<functional>
#define inf 1e9
#define ll long long
#define pii pair<int,int>
using namespace std;
const int N = 1e3+10;
const int mod = 1e4;
int t, n, m, k;
int a[N], f[N];
void solve(){
cin>>n>>m;
for(int i = 1;i <= n;i++)a[i] = i;
int fz = n-1, fm = 1;f[1] = f[n] = 1;
for(int i = 2;i <= n-1;i++)
f[i] = f[i-1]*fz/fm, fz--, fm++;
do{
int res = 0;
for(int i = 1;i <= n;i++){
if(i==1||i==n)res += a[i];
else res += a[i]*f[i];
}
if(res==m){
for(int i = 1;i <= n;i++){
cout<<a[i]<<" ";
}cout<<"\n";
}
}while(next_permutation(a+1,a+n+1));
for(int i = 1;i <= n;i++){
cout<<a[i]<<" ";
}
}
int main(){
// cin>>t;
t = 1;
while(t--)
solve();
return 0;
}