P3581 [POI2015] CZA 题解
思路:
找性质 dp。
我们首先观察, 显然除非 否则无解, 的话只能把奇数和偶数分别放在一起,只有两种情况。
所以最难的是 的情况。
我们发现,整个环大致是这样的:从 出发两条不相交的路径,经过一些点,然后在 汇合。
而且我们注意到,由于 很小,这两个序列是大致单调的。
更具体,如果连续三个数都在同一路径上,那么显然是无解的。
所以我们可以考虑 dp。设 表示上面走到 ,下面走到 的方案数。
但是这样有可能重复,所以我们钦定 且 的下一个 。
我们发现只存在三种情况:, 且 是否填完。
于是开始快乐的分类讨论。
对于 的转移,我们按照 的下一个是谁讨论。
如果 的下一个是 ,则有这样的情况:
一定要注意我们的条件,下一个要 .
如果下一个是 ,则有这样的情况:
这样这一种全部转移完了。
我们考虑 没有用掉的情况,有以下几种:
如果 被用掉,有:
现在我们已经转移完了,但是我们还需要考虑两个事儿:初始和统计答案。
对于初始,我们都是从 出发,我们枚举下方第一条边的情况:
如果是 2:
如果是 3:
如果是 4:
这样初始就解决了。
现在考虑统计答案,我们枚举 的状态即可:
这样就完成了所有讨论,时间复杂度 。
点击查看代码
#include <iostream>
#include <cstdio>
#include <vector>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 1e6 + 5;
const int M = 1e5 + 5;
const int mod = 1e9 + 7;
void add(int &x, int y) {
x = (x + y) % mod;
}
int n = 0, m, p;
bool mtr[N][7] = {{0}};//表示 mtr[i][j] 表示 i + 3 - j 能否坐在右边
bool chk(int x, int y) {//y 坐在 x 右边
return !mtr[x][y - x + 3] && x <= n && y <= n;
}
bool chk2(int x, int y) {
return chk(y, x);
}
namespace SLV1 {
int f[N][3] = {{0}};
//f[i][0] 表示 (i, i + 1)
//f[i][1] 表示 (i, 0, i + 2)
//f[i][2] 表示 (i, x, i + 2)
void slv() {
// for (int x = 1; x <= n; x++) {
// for (int y = 1; y <= n; y++) {
// cout << x << " " << y << " " << chk(x, y) << endl;
// }
// }
//初始的转移
if (chk2(1, 2))
add(f[1][0], 1);
if (chk2(1, 3)) {
add(f[1][1], 1);
if (chk(1, 2))
add(f[2][0], 1);
}
if (chk2(1, 4)) {
if (chk(1, 2)) {
add(f[2][1], 1);
if (chk(2, 3))
add(f[3][0], 1);
}
if (chk(1, 3)) {
if (chk(3, 2))
add(f[2][2], 1);
if (chk2(4, 2) && chk2(2, 5))
add(f[3][2], 1);
}
}
for (int i = 1; i <= n; i++) {
//f[i][0] 的转移
if (chk(i, i + 2)) {
if (chk2(i + 1, i + 3))
add(f[i + 2][0], f[i][0]);
if (chk2(i + 1, i + 4)) {
add(f[i + 2][1], f[i][0]);
if (chk(i + 2, i + 3))
add(f[i + 3][0], f[i][0]);
}
}
if (chk(i, i + 3)) {
if (chk2(i + 1, i + 4) && chk(i + 3, i + 2))
add(f[i + 2][2], f[i][0]);
if (chk2(i + 1, i + 4) && chk2(i + 4, i + 2) && chk2(i + 2, i + 5))
add(f[i + 3][2], f[i][0]);
if (chk2(i + 1, i + 2) && chk2(i + 2, i + 4))
add(f[i + 3][0], f[i][0]);
if (chk2(i + 1, i + 2) && chk2(i + 2, i + 5)) {
add(f[i + 3][1], f[i][0]);
if (chk(i + 3, i + 4))
add(f[i + 4][0], f[i][0]);
}
}
//f[i][1] 的转移
if (chk(i, i + 3) && chk(i + 3, i + 1) && chk(i + 1, i + 4) && chk2(i + 2, i + 5))
add(f[i + 4][0], f[i][1]);
if (chk(i, i + 3) && chk2(i + 2, i + 1) && chk2(i + 1, i + 4))
add(f[i + 3][0], f[i][1]);
//f[i][2] 的转移
if (chk(i, i + 3) && chk2(i + 2, i + 4))
add(f[i + 3][0], f[i][2]);
if (chk(i, i + 3) && chk2(i + 2, i + 5)) {
add(f[i + 3][1], f[i][2]);
if (chk(i + 3, i + 4))
add(f[i + 4][0], f[i][2]);
}
// cout << i << " f[0]:" << f[i][0] << " f[1]:" << f[i][1] << " f[2]:" << f[i][2] << endl;
}
//统计答案
int ans = 0;
if (chk(n - 1, n))
add(ans, f[n - 1][0]);
if (chk(n - 2, n)) {
add(ans, f[n - 2][2]);
if (chk2(n - 1, n))
add(ans, f[n - 2][0]);
}
// cout << "look " << ans << endl;
if (chk(n - 3, n) && chk2(n - 2, n - 1) && chk(n - 1, n))
add(ans, f[n - 3][0]);
// cout << "look2 " << ans << endl;
if (chk(n - 3, n) && chk2(n - 1, n))
add(ans, f[n - 3][2]);
// cout << "look3 " << ans << endl;
if (chk(n - 3, n) && chk2(n - 1, n - 2) && chk2(n - 2, n))
add(ans, f[n - 3][1]);
if (chk(n - 4, n - 1) && chk(n - 1, n - 3) && chk(n - 3, n) && chk2(n - 2, n))
add(ans, f[n - 4][1]);
printf("%d\n", ans);
}
}
namespace SLV2 {
int t[N] = {0};
void slv() {
for (int i = 1; i < n; i++)
t[i] = i;
int ans = 0;
do {
bool flg = true;
t[n] = n;
for (int i = 1; i <= n; i++)
if (abs(t[i] - t[i % n + 1]) > p || !chk(t[i], t[i % n + 1]))
flg = false;
ans += flg;
// if (flg && t[5] == 4) {
// for (int i = 1; i <= n; i++)
// cout << t[i] << " ";
// cout << endl;
// }
} while (next_permutation(t + 1, t + n));
cout << ans << endl;
}
}
int ans[N] = {0};
int main() {
scanf("%d%d%d", &n, &m, &p);
for (int i = 1; i <= m; i++) {
int u, v;
scanf("%d%d", &u, &v);
if (abs(u - v) <= p)
mtr[u][v - u + 3] = true;
}
// for (int i = 1; i <= n; i++)
// for (int j = 1; j <= n; j++)
// cout << i << " " << j << " " << chk(i, j) << endl;
if (n <= 8) {
SLV2::slv();
return 0;
}
if (p == 0) {
if (n == 1)
printf("1\n");
else
printf("0\n");
return 0;
}
if (p == 1) {
if (n <= 2)
printf("1\n");
else
printf("0\n");
return 0;
}
if (p == 2) {
if (n == 2) {
if (chk(1, 2) && chk(2, 1)) {
cout << 1 << endl;
}
else
cout << 0 << endl;
return 0;
}
int cur = 1, len = 0;
while (cur <= n) {
ans[++len] = cur;
cur += 2;
}
cur = (cur == n + 1 ? n : n - 1);
while (cur > 1) {
ans[++len] = cur;
cur -= 2;
}
//for (int i = 1; i <= len; i++)
// cout << ans[i] << " ";
//cout << endl;
bool flg = true;
for (int i = 1; i <= len; i++)
if (!chk(ans[i], ans[i % n + 1]))
flg = false;
int tmp = flg;
flg = true;
for (int i = 1; i <= len; i++)
if (!chk2(ans[i], ans[i % n + 1]))
flg = false;
tmp += flg;
printf("%d\n", tmp);
return 0;
}
if (p == 3) {
SLV1::slv();
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】