Date:2022.02.13
题目描述
设集合
N
=
M
=
{
x
∣
∈
N
+
,
x
<
=
k
,
k
∈
N
+
}
N=M=\{x|\in N_+,x<=k,k\in N_+\}
N=M={x∣∈N+,x<=k,k∈N+}
设f为N到M的映射。
求满足:
f[f(x)]=x的不同的映射ff的个数,由于答案较大,输出答案对14233333取余的数即可。
输入格式
输入一个正整数k
输出格式
输出满足f[f(x)]=x的不同的映射ff的个数对14233333取余得到的数。
输入输出样例
输入 #1复制
3
输出 #1复制
4
说明/提示
四个映射分别为:
f(1) f(2) f(3)
1 2 3
1 3 2
2 1 3
3 2 1
数据范围:
对于20%的数据,
1
<
=
k
<
=
9
1<=k<=9
1<=k<=9
对于其它的80%的数据,
1
<
=
k
<
=
1
0
7
1<=k<=10^7
1<=k<=107
内存20MB…(一开始开1MB把自己坑了)
题意解释:N、M集合中分别有
k
k
k个数,之间可两两配对(每个元素不可重复配对在>=2对中)或自己配对(或者理解为不配对,反正占用1种情况)。
思路:设状态方程为:
f
[
i
]
f[i]
f[i]:集合N、M中有
i
i
i个数时符合条件的映射数。
f
[
f
[
x
]
]
=
=
x
f[f[x]]==x
f[f[x]]==x即表示映射的关系,因此只能有两种分类:
①
f
[
x
]
=
=
x
−
>
f
[
f
[
x
]
]
=
=
x
f[x]==x -> f[f[x]]==x
f[x]==x−>f[f[x]]==x,即
x
x
x的映射即为自己(或者说
x
x
x无映射),这显然是1种情况。对答案贡献为
f
[
i
−
1
]
∗
1
f[i-1]*1
f[i−1]∗1。
②
f
[
x
]
=
=
y
&
&
f
[
y
]
=
=
x
f[x]==y \&\& f[y]==x
f[x]==y&&f[y]==x,这体现为N、M的映射关系(可看为一个X形状的交叉)。对答案贡献为
f
[
i
−
2
]
∗
(
i
−
1
)
f[i-2]*(i-1)
f[i−2]∗(i−1)【即第
i
i
i个与
(
1
,
i
−
1
)
(1,i-1)
(1,i−1)中任一个配对,因此
(
i
−
1
)
(i-1)
(i−1)种配对情况;至于为什么是
f
[
i
−
2
]
f[i-2]
f[i−2]?个人认为更好理解的是:我们关心的无非是在前面
(
1
,
i
−
1
)
(1,i-1)
(1,i−1)中选中的是否在之前已配对?如果未配对,直接配对,那么这样
(
1
,
i
−
1
)
(1,i-1)
(1,i−1)中就转化为
i
−
2
i-2
i−2个元素的配对问题,顺序显然影响不了什么,因此完全可等价为
f
[
i
−
2
]
f[i-2]
f[i−2](尽管不一定是前
i
−
2
i-2
i−2个,但完全可以等价);如果已配对,相当于拆散了这个配对,使被选择的和
i
i
i配对,原来与被选择的元素配对的元素当然可以再找其它配对呀,剩下的理由同上,仍可以等价为
f
[
i
−
2
]
f[i-2]
f[i−2]】。
因此状态转移方程:
f
[
i
]
=
f
[
i
−
1
]
+
f
[
i
−
2
]
∗
(
i
−
1
)
;
f[i]=f[i-1]+f[i-2]*(i-1);
f[i]=f[i−1]+f[i−2]∗(i−1);
初始状态:
f
[
0
]
=
=
1
f[0]==1
f[0]==1;(相当于无法配对或给自己配对,看怎么理解了)
f
[
1
]
=
2
;
f[1]=2;
f[1]=2;(不配对或X形状配对)。【这里为了方便滚动数组取模,
i
i
i都减了1,对应的实际意义应为
f
[
i
+
1
]
f[i+1]
f[i+1]】。
除此之外,空间太小了,显然开不下1e7,滚动数组优化下(不然喜提re
代码如下:
#include <bits/stdc++.h>
using namespace std;
const int N = 1010,mod=14233333;
typedef long long LL;
LL n,m,p,k,f[3];
int main()
{
cin>>k;f[0]=1;f[1]=2;LL res=2;
for(int i=3;i<=k;i++)
{
f[res]=(f[(res-1+3)%3]+f[(res-2+3)%3]*(i-1)+mod)%mod;
res++;res%=3;
}
cout<<f[(k-1+3)%3];
return 0;
}