Keep Connect
[Link](F - Keep Connect (atcoder.jp))
题意
给你一个 n n n和 p p p,表示有一个长 n − 1 n-1 n−1宽 1 1 1的梯子,请你依次回答删除 1 ∼ n − 1 1\sim n-1 1∼n−1条边后依旧联通的方案数。
思路
- 状压 d p dp dp
我们按照从前往后枚举每一列来统计方案,考虑删到了第 i i i列,什么样的情况可以继续往后统计呢?1.前 i i i列是联通的 。2.前 i i i列一部分和第 i i i列上面的点连通,另一部分和第 i i i列下面的点连通。只有这样后面才是成立的,否则从第 i i i列开始就将这个梯子分开了,已经不合法了。第 i i i列的删除法会形成第 i + 1 i+1 i+1列的情况,很明显的转移关系。
考虑 f [ i ] [ j ] [ 0 / 1 ] : 前 i 列 删 了 j 个 边 且 是 否 联 通 的 方 案 数 f[i][j][0/1]:前i列删了j个边且是否联通的方案数 f[i][j][0/1]:前i列删了j个边且是否联通的方案数,注意不连通是我们上面所探讨的那种对后续状态可以转移有贡献的不连通的情况。
第 i i i列向第 i + 1 i+1 i+1列转移是一个 ] 三条边的选取,设 p 1 , p 2 , p 3 p1,p2,p3 p1,p2,p3分别为上下和右边的边, 0 0 0为不选, 1 1 1为选
转移分为:
-
f [ i ] [ j ] [ 0 ] f[i][j][0] f[i][j][0] 由于本身不连通,因此 p 1 , p 2 p1,p2 p1,p2必须选,否则就会造成不合法的情况,因此 i + 1 i+1 i+1的联通性取决于 p 3 p3 p3选不选
f [ i + 1 ] [ j + 1 − p 3 ] [ p 3 ] = f [ i + 1 ] [ j + 1 − p 3 ] [ p 3 ] + f [ i ] [ j ] [ 0 ] f[i+1][j+1-p3][p3]=f[i+1][j+1-p3][p3]+f[i][j][0] f[i+1][j+1−p3][p3]=f[i+1][j+1−p3][p3]+f[i][j][0]
-
f [ i ] [ j ] [ 1 ] f[i][j][1] f[i][j][1] 由于本身连通,因此 p 1 , p 2 p1,p2 p1,p2只需要有一个选即可,否则就会造成不合法情况(即断层),因此 p 1 , p 2 , p 3 p1,p2,p3 p1,p2,p3只要选了 ≥ 2 \ge 2 ≥2 条就一定连通,否则不连通
f [ i + 1 ] [ j + 3 − p 1 − p 2 − p 3 ] [ p 1 + p 2 + p 3 > = 2 ] = f [ i + 1 ] [ j + 3 − p 1 − p 2 − p 3 ] [ p 1 + p 2 + p 3 > = 2 ] + f [ i ] [ j ] [ 1 ] f[i+1][j+3-p1-p2-p3][p1+p2+p3>=2]=f[i+1][j+3-p1-p2-p3][p1+p2+p3>=2]+f[i][j][1] f[i+1][j+3−p1−p2−p3][p1+p2+p3>=2]=f[i+1][j+3−p1−p2−p3][p1+p2+p3>=2]+f[i][j][1]
设一下边界,即一个的情况,然后每个状态往后转移即可。
Code
#include <bits/stdc++.h>
#define x first
#define y second
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long double ld;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<double, double> PDD;
typedef unsigned long long ULL;
const int N = 1e5 + 10, M = 2 * N, INF = 0x3f3f3f3f, mod = 1e9 + 7;
const double eps = 1e-8, pi = acos(-1), inf = 1e20;
int n, m, k, p;
int a[N];
int f[3010][3010][2];
int main() {
ios::sync_with_stdio(false), cin.tie(0);
cin >> n >> p;
f[1][1][0] = f[1][0][1] = 1;
for (int i = 1; i < n; i ++)
for (int j = 0; j < n; j ++)
for (int k = 0; k < 2; k ++)
if (f[i][j][k])
for (int p1 = 0; p1 < 2; p1 ++)
for (int p2 = 0; p2 < 2; p2 ++)
for (int p3 = 0; p3 < 2; p3 ++) {
if (!k && (!p1 || !p2)) continue;
if (k && !p1 && !p2) continue ;
if (!k) f[i + 1][j + 1 - p3][p3] = ((LL)f[i + 1][j + 1 - p3][p3] + f[i][j][k]) % p;
if (k) f[i + 1][j + 3 - p1 - p2 - p3][(p1 + p2 + p3) >= 2] = ((LL)f[i + 1][j + 3 - p1 - p2 - p3][(p1 + p2 + p3) >= 2] + f[i][j][k]) % p;
}
for (int i = 1; i < n; i ++)
cout << f[n][i][1] << ' ';
cout << '\n';
return 0;
}