[DTOI 2023] D. Goodbye 2022
题目背景
我用烟花宣告,用挥手告别,用鞠躬感谢,过去的都已经过去,接下来的路我要悠闲地走,愉悦地走,脚步如同时间不会停止,下一年,我们还会再会。
题目描述
这次的题目背景和 luanmenglei 没有一点关系。
给定 n , k , p n,k,p n,k,p,求有多少有序 p p p 元组 ( a 1 , a 2 , ⋯ , a p ) (a_1,a_2,\cdots,a_p) (a1,a2,⋯,ap) 满足
-
∀ i ∈ [ 1 , p ] \forall i \in [1,p] ∀i∈[1,p], a i ∈ [ 1 , n ] a_i\in [1,n] ai∈[1,n]。
-
∀ i ∈ [ 1 , p ) \forall i\in [1,p) ∀i∈[1,p), popcount ( a i ⊕ a i + 1 ) = k \operatorname{popcount}(a_i\oplus a_{i+1})=k popcount(ai⊕ai+1)=k。
-
∀ i , j ∈ [ 1 , p ] , i ≠ j \forall i,j\in[1,p],i\neq j ∀i,j∈[1,p],i=j, a i ≠ a j a_i\neq a_j ai=aj。
答案对 998244353 998244353 998244353 取模。
- 其中 popcount ( x ) \operatorname{popcount}(x) popcount(x) 表示 x x x 在二进制表达下 1 1 1 的个数。
- ⊕ \oplus ⊕ 表示按位异或操作。
- 两个有序 p p p 元组 ( a 1 , a 2 , … , a p ) (a_1,a_2,\dots,a_p) (a1,a2,…,ap), ( b 1 , b 2 , … , b p ) (b_1,b_2,\dots,b_p) (b1,b2,…,bp) 不同当且仅当存在 i ∈ [ 1 , p ] i\in[1,p] i∈[1,p] 使得 a i ≠ b i a_i\neq b_i ai=bi。
输入格式
一行三个正整数 n , k , p n,k,p n,k,p。
输出格式
一行一个数,表示答案。
样例 #1
样例输入 #1
5 1 2
样例输出 #1
8
样例 #2
样例输入 #2
6 1 3
样例输出 #2
12
样例 #3
样例输入 #3
7 1 4
样例输出 #3
48
样例 #4
样例输入 #4
8 3 5
样例输出 #4
6
样例 #5
样例输入 #5
9 2 5
样例输出 #5
72
样例 #6
样例输入 #6
114 3 3
样例输出 #6
106624
样例 #7
样例输入 #7
514 3 4
样例输出 #7
296097032
样例 #8
样例输入 #8
1000 7 5
样例输出 #8
569405945
样例 #9
样例输入 #9
1000 7 1
样例输出 #9
1000
提示
对于所有测试数据,保证 1 ≤ n ≤ 1000 1\leq n \leq 1000 1≤n≤1000, 1 ≤ k ≤ ⌊ log 2 n ⌋ 1\leq k\leq \lfloor \log_2 n\rfloor 1≤k≤⌊log2n⌋, 1 ≤ p ≤ 5 1 \leq p \leq 5 1≤p≤5。
每个测试点的具体限制见下表:
测试点编号 | n ≤ n\leq n≤ | p = p = p= |
---|---|---|
1 1 1 | 1000 1000 1000 | 1 1 1 |
2 ∼ 3 2 \sim 3 2∼3 | 1000 1000 1000 | 2 2 2 |
4 ∼ 5 4 \sim 5 4∼5 | 300 300 300 | 3 3 3 |
6 ∼ 12 6 \sim 12 6∼12 | 1000 1000 1000 | 3 3 3 |
13 ∼ 15 13 \sim 15 13∼15 | 1000 1000 1000 | 4 4 4 |
16 ∼ 21 16 \sim 21 16∼21 | 300 300 300 | 5 5 5 |
22 ∼ 25 22 \sim 25 22∼25 | 1000 1000 1000 | 5 5 5 |
思路
- 首先,这个条件:
∀
i
∈
[
1
,
p
)
\forall i\in [1,p)
∀i∈[1,p),
popcount
(
a
i
⊕
a
i
+
1
)
=
k
\operatorname{popcount}(a_i\oplus a_{i+1})=k
popcount(ai⊕ai+1)=k。这个我们可以用C++的
__builtin_popcount(i^j)==k
来维护,它可以在 O ( 1 ) O(1) O(1) 的时间复杂度算出来。 - 其次, 1 ≤ p ≤ 5 1 \leq p \leq 5 1≤p≤5,说明我们就可以分类讨论即可。
本人的遇到的问题
- 我们要特别注意,第二个条件是: ∀ i ∈ [ 1 , p ) \forall i\in [1,p) ∀i∈[1,p),没有包括 p p p 。
- 图片里面的 f [ a 1 ] f[a_1] f[a1] 算的就是 a 2 a_2 a2 的合法数量。(我个人认为因为有条件 1 1 1 的限制,再加上条件 3 3 3 的限制,所以 f [ a 1 ] f[a_1] f[a1] 算的数是 a 2 a_2 a2 的数,因为此时的 a 1 a_1 a1 要满足条件 2 2 2,所以 a 2 a_2 a2 就只能跟 f [ a 1 ] f[a_1] f[a1] 相等)
- 我感觉 f [ a x ] f[a_x] f[ax] 来算不固定枚举的 a a a 的种类数,由于条件 2 2 2 ,所以它的周围(比如图片中 p = 3 p=3 p=3 时,枚举 a 2 a_2 a2 ,那么因为条件 2 2 2,此时 a 1 , a 3 a_1,a_3 a1,a3 的方案数就会受它影响;再比如图片中的 p = 5 p=5 p=5 时的第一点,它要算 a 1 a_1 a1 ,此时 a 1 a_1 a1 是受 a 2 a_2 a2 的枚举影响,但它为什么要减去 a 3 , a 4 a_3,a_4 a3,a4 ,因为当满足 b [ a 2 ] [ a 4 ] = 1 b[a_2][a_4]=1 b[a2][a4]=1 的时候,此时 a 1 a_1 a1 绝对不能等于 a 4 a_4 a4,因为你如果相等的话,那么此时 a 1 ⊕ a 2 = 0 a_1\oplus a_2=0 a1⊕a2=0,此时就不满足题意了(具体的可以看第一张图,因为它的不等关系比第二张更清晰))
- 图片中说的枚举 a x a_x ax ,所以其他的值都得按照它们来。
- 图片中所说 b [ a 2 ] [ a 3 ] = 1 b[a_2][a_3]=1 b[a2][a3]=1,此时说明满足第二个条件,等于 0 0 0 则不满足。
- 图片中的 f [ i ] f[i] f[i] 怎么算呢,我们可以用 bitset 的 count() 函数来算(它算的是 1 的总个数)。
- 为什么 b [ a 2 ] [ a 4 ] b[a_2][a_4] b[a2][a4]能分类讨论呢?因为 a 2 a_2 a2 和 a 4 a_4 a4 不是相邻的。
代码
//当p=1时,条件2、3无用
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N = 1010,mod = 998244353;
bitset<N> s[N];
int n,k,p;
int f[N];
int b[N][N];
int ans;
signed main(){
cin>>n>>k>>p;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i!=j)
s[i][j]=(__builtin_popcount(i^j)==k);//能在O(1)找到这个条件所有情况。满足就为1
}
}
//当p=1时,说明只有一个数,所以答案为n
if(p==1){
cout<<n<<endl;
}else if(p==2){
//p=2时,2个数,通过枚举a1来约束a2
for(int i=1;i<=n;i++){
ans=(ans+s[i].count())%mod;
}
cout<<ans;
}else if(p==3){
//p=3时,枚举合法的a2,因为a1!=a3,所以它们总的方案数为p2*p2-p2
//ans=p2(p2-1)
for(int i=1;i<=n;i++){
ans=(ans+s[i].count()*(s[i].count()-1))%mod;
}
cout<<ans;
}else if(p==4){
//p=4,我们枚举a2、a3,这样就不担心连锁反应了
int t;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(s[i][j]){
t=(s[i].count()-1)*(s[j].count()-1)%mod;
t=(t-(s[i]&s[j]).count()+mod)%mod;//因为这a1和a4很可能是相等的。
ans=(ans+t)%mod;
}
}
}
cout<<ans;
}else{
//p=5,我们枚举a2和a4,此时a3有a2^a4种选法
int t1,t2;
for(int i=1;i<=n;i++){
for(int j=1;j<=n;j++){
if(i!=j){
ans=(ans+((s[i].count()-1-s[i][j])*(s[j].count()-1-s[i][j])-(s[i]&s[j]).count()+1)*((s[i]&s[j]).count()))%mod;
}
}
}
cout<<ans;
}
return 0;
}