P3581 [POI2015] CZA 题解

P3581 [POI2015] CZA

思路:

找性质 dp。

我们首先观察,\(p \le 1\) 显然除非 \(n=1\) 否则无解,\(p=2\) 的话只能把奇数和偶数分别放在一起,只有两种情况。

所以最难的是 \(p=3\) 的情况。

我们发现,整个环大致是这样的:从 \(1\) 出发两条不相交的路径,经过一些点,然后在 \(n\) 汇合。

而且我们注意到,由于 \(p\) 很小,这两个序列是大致单调的。

更具体,如果连续三个数都在同一路径上,那么显然是无解的。

所以我们可以考虑 dp。设 \(f(i,j)\) 表示上面走到 \(i\),下面走到 \(j\) 的方案数。

但是这样有可能重复,所以我们钦定 \(i < j\)\(i\) 的下一个 \(> j\)

我们发现只存在三种情况:\(j = i + 1\)\(j = i + 2\)\(i+1\) 是否填完。

于是开始快乐的分类讨论。

对于 \(j = i + 1\) 的转移,我们按照 \(i\) 的下一个是谁讨论。

如果 \(i\) 的下一个是 \(i + 2\),则有这样的情况:

一定要注意我们的条件,下一个要 $ > j$.

如果下一个是 \(i+3\),则有这样的情况:

这样这一种全部转移完了。

我们考虑 \(i+1\) 没有用掉的情况,有以下几种:

如果 \(i+1\) 被用掉,有:

现在我们已经转移完了,但是我们还需要考虑两个事儿:初始和统计答案。

对于初始,我们都是从 \(1\) 出发,我们枚举下方第一条边的情况:

如果是 2:

如果是 3:

如果是 4:

这样初始就解决了。

现在考虑统计答案,我们枚举 \(i\) 的状态即可:

这样就完成了所有讨论,时间复杂度 \(O(n)\)

点击查看代码
#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;
}  
posted @ 2024-09-28 13:40  rlc202204  阅读(6)  评论(0编辑  收藏  举报