洛谷P8941 [SSOI 2023 easy Round] D. Goodbye 2022 题解
题目链接:P8941 [SSOI 2023 easy Round] D. Goodbye 2022
题意:
给定 n,k,p,求有多少有序 p 元组 (a1,a2,⋯,ap) 满足
∀i∈[1,p],ai∈[1,n]。
∀i∈[1,p),popcount(ai⊕ai+1)=k。
∀i,j∈[1,p],i≠j,ai≠aj。
答案对 998244353 取模。
- 其中 popcount(x) 表示 x 在二进制表达下 1 的个数。
- ⊕ 表示按位异或操作。
- 两个有序 p 元组 (a1,a2,…,ap),(b1,b2,…,bp) 不同当且仅当存在 i∈[1,p] 使得 ai≠bi。
输入格式:
一行三个正整数 n,k,p。
输出格式:
一行一个数,表示答案。
数据范围:
对于所有测试数据,保证 1≤n≤1000,1≤k≤⌊log2n⌋,1≤p≤5。
注意到 n 很小,所以记一个bitset<N> b[N]
有 bi,j=[popc(i⊕j)=k] ,然后暴力算出来,并记
接着分类讨论 p 的情况。
p=1 ,答案为 n 。
p=2 ,枚举 a1 ,答案为 ∑ni=1fi 。
p=3 ,枚举 a2 ,则 a1 的方案数为 fa2 ,a3 的方案数为 fa2−1 ,故答案就是 ∑ni=1fi(fi−1)
p=4 ,枚举 a2,a3 ,显然 ba2,a3=True ,否则不合法。
此时 a1 取值有 fa2−1 个(因为 a1≠a3),a4 取值有 fa3−1 个( 因为 a2≠a4 )
统计时再去掉 a1=a4 的情况,答案就是 ∑ni=1∑nj=1[bi,j=True]×[(fi−1)(fj−1)−gi,j)]
p=5 ,枚举 a2,a4 ,进一步分类讨论 ba2,a4 :
- ba2,a4=True ,此时 a1 的取值有 fa2−2 个(除了 a3,a4),a5 同理有 fa4−2 个。
- ba2,a4=False ,此时 a1 的取值有 fa2−1 个(除了 a3),a5 同理有 fa4−1 个。
去掉 a1=a5 的情况,由于 a3 已经占用了一个 ga2,a4 的位置,故该情况有 ga2,a4−1 个,则
{gi,j×[(fi−2)(fj−2)−(gi,j−1)],ba2,a4=Truegi,j×[(fi−2)(fj−2)−(gi,j−1)],ba2,a4=False
时间复杂度 O(n3ω)
代码:
cpp
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
const int mod = 998244353;
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
void add(int &x, int y) { (x += y) >= mod ? x -= mod : 0; }
#define popc(x) __builtin_popcountll(x)
#define N ((int)(1e3 + 15))
bitset<N> b[N];
int n,k,p,res,f[N],g[N][N];
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
cin >> n >> k >> p;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++) b[i][j] = (popc(i ^ j) == k);
for(int i = 1; i <= n; i++) f[i] = b[i].count();
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++) g[i][j] = (b[i] & b[j]).count();
if(p == 1) return cout << (res = n) << '\n', 0;
if(p == 2) { for(int i = 1; i <= n; i++) add(res, f[i]); cout << res << '\n'; }
else if(p == 3) {
for(int i = 1; i <= n; i++) add(res, f[i] * (f[i] - 1) % mod);
cout << res << '\n';
}else if(p == 4) {
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++) if(b[i][j])
add(res, (((f[i] - 1) * (f[j] - 1) - g[i][j]) % mod + mod) % mod);
cout << res << '\n';
}else if(p == 5) {
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++) if(i != j) {
int x = (f[i] - (b[i][j] ? 2 : 1)) * (f[j] - (b[i][j] ? 2 : 1)) % mod;
add(res, (g[i][j] * (x - g[i][j] + 1) % mod + mod) % mod);
}
cout << res << '\n';
}
return 0;
}
参考文献: