概述
一道思维含量很大,代码量很小的紫题。赛时AC人数较少,本蒟蒻也并没有做出来……
写个题解总结一下。
Description
给定一种生成长度为 3 n 3n 3n的排列的方法:先随意生成一个排列,然后把这个排列划分为 n n n块,每块 3 3 3个数, 1 1 1个指针。刚开始每个指针指向的是该段的开头,每次找到所有指针指向的数中最小的那个,把这个数放到这个序列的末尾并将这个指针向右移动一位。如果一个指针出了它原先所在的块,就删除该指针。当所有指针都不存在后,生成完成。
求存在多少个长度为 3 n 3n 3n 的排列是可以按照上述方式生成的。注意将答案对 m o d mod mod 取模。
Solution
我们看完本题的第一反应是——我们要挖性质。
首先,我们自己对于几个排列模拟一下。可以发现,我们最终生成的序列中,可能存在一些递减的段。即,有一个小的数被卡在了大的数的后面,只有这个大的数出来,它才能出来。
但是这个大的数为什么会出来?是因为它是在所有指针指向的数中最小的。那么必然,紧接着出来的那个小的数只能紧接着它后面出来。大概证明一下,假设这个大的数为 x x x,小的数为 y y y,当 x x x成为最小的出队后,指针指向了 y y y,此时 y y y一定成为了指针指向的数中最小的 ( y < x ) (y<x) (y<x),那么 y y y就出队了——即 x x x与 y y y必然在同一个块中。
所以,对于生成的序列中每一个递减的段的长度都不能超过 3 3 3,因为初始所有块的长度只有 3 3 3。这是一个必要的条件。
但是充分吗?你再写几个序列,就发现自己被 H a c k Hack Hack了。千万不要像填数游戏那样,挖一个性质就走人,最终发现这个条件不是充分的。
另外一个性质是什么呢?我们继续模拟,可以发现: 假设长度为 2 2 2的递减段有 x x x个,长度为 1 1 1的递减段有 y y y个,那么肯定有 x ≤ y x≤y x≤y且 3 ∣ ( y − x ) 3|(y-x) 3∣(y−x)。
为什么呢?因为,对于 x x x个长度为 2 2 2的递减段,它们肯定两两出现在同一个块中。而一个块中的另一个数,就是 y y y个长度为 1 1 1的递减段之一。 y y y是绝对不能少的,否则就拼不完;如果 y y y多的话,可以把 y y y个长度为 1 1 1的段摆在别的块中。
而 3 ∣ ( y − x ) 3|(y-x) 3∣(y−x)又是为什么呢?因为,我们对于多出来的 y − x y-x y−x个长度为 1 1 1的段,我们必须把这些长度为 1 1 1的段摆完,而每三个这些段必须出现在同一个块中,否则就另外需要别的长度为 2 2 2的段或别的长度为 1 1 1的段去填充——而长度为 2 2 2的 x x x个段已经摆完了,长度为 1 1 1的段也摆完了;那么,这种序列就无法生成了。
把这两个性质结合起来,就得到了可生成序列的充分必要条件:
①任意递减段的长度不超过
3
3
3;
②
x
≤
y
x≤y
x≤y且
3
∣
(
y
−
x
)
3|(y-x)
3∣(y−x),这里
x
x
x为长度为
2
2
2的段的数量,
y
y
y为长度为
1
1
1的段的数量。
现在这道题成为了一道纯粹的计数题。
根据CSP-S 2019 D2T1(Emiya今天的饭)的经验,我们可以采用差值 d p dp dp。
状态设计:
d
p
i
,
j
dp_{i,j}
dpi,j: 目前考虑了前
i
i
i个数,
j
j
j为
y
−
x
y-x
y−x的值。
状态转移:
①如果这是一个长度为
1
1
1的段,那么
d
p
i
,
j
=
d
p
i
−
1
,
j
−
1
dp_{i,j}=dp_{i-1,j-1}
dpi,j=dpi−1,j−1。
②如果这是一个长度为
2
2
2的段,那么
d
p
i
,
j
=
d
p
i
−
2
,
j
+
1
×
(
i
−
1
)
dp_{i,j}=dp_{i-2,j+1}×(i-1)
dpi,j=dpi−2,j+1×(i−1)。
这里为啥要乘上一个
(
i
−
1
)
(i-1)
(i−1)呢?因为,我们已经安排好了前
i
−
2
i-2
i−2个数,而第
i
−
1
i-1
i−1个数又不得不是最大的(这是一个长度为
2
2
2的递减段),只有
1
1
1种安排方式(选最大的数);所以,第
i
i
i个数有
i
−
1
i-1
i−1种安排方式,要乘上
i
−
1
i-1
i−1。
③如果这是一个长度为
3
3
3的段,那么
d
p
i
,
j
=
d
p
i
−
3
,
j
×
(
i
−
1
)
×
(
i
−
2
)
dp_{i,j}=dp_{i-3,j}×(i-1)×(i-2)
dpi,j=dpi−3,j×(i−1)×(i−2)。
最终答案为
∑
i
=
0
3
n
d
p
3
n
,
i
[
i
m
o
d
3
=
0
]
sum_{i=0}^{3n} dp_{3n,i}[i mod 3=0]
∑i=03ndp3n,i[i mod 3=0]。
时间复杂度 O ( n 2 ) O(n^2) O(n2)。注意取模即可。
Code
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int maxl=3005;
int n,mod,x,ans=0;
int dp[maxl*3][6*maxl];//dp[i][j]:i numbers, j为段1-段2
signed main()
{
cin>>n>>mod;
x=3*n;
dp[0][x]=1;
for (int i=0;i<3*n;i++)
{
for (int j=-3*n;j<=(3*n);j++)
{
dp[i+1][j+1+x]=(dp[i+1][j+1+x]+dp[i][j+x])%mod;
dp[i+2][j-1+x]=(dp[i+2][j-1+x]+dp[i][j+x]*(i+1))%mod;
dp[i+3][j+x]=(dp[i+3][j+x]+dp[i][j+x]*(i+1)%mod*(i+2))%mod;
}
}
for (int i=0;i<=n;i++) ans=(ans+dp[3*n][i*3+x])%mod;
cout<<ans<<endl;
return 0;
}
最后
以上就是高大指甲油为你收集整理的Atcoder AGC 043 D题解DescriptionSolutionCode的全部内容,希望文章能够帮你解决Atcoder AGC 043 D题解DescriptionSolutionCode所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复