JZOJ 4228. 【五校联考3day2】C (Standard IO)

题意

在平面直角坐标系上有(n)个点,没有重合的点。你要从这(n)个点向上下左右四个方向的其中一个引一条射线,最终没有射线相交,求方案数。
(nleq 54)

Solution

有dick意思的dp。
暴力是(O(4^n)),太水就不说了。

正解:
先把(n)个点按照(y)坐标从大到小排序,离散化横坐标。
(f_{i,j,k,l,m})表示已经确定了前(i)个点的方向,其中向右的点中最小横坐标的是(j),向左的点中最大的横坐标是(k),向下的点中最小的横坐标是(l),向下的点中最大的横坐标是(m),这种情况下的方案数。

初始状态(f[0][max+1][0][max+1][0]=1),其他都是(0)。(max是离散化后横坐标最大值+1)

首先还要预处理一个(ok[i][0..3])(英语不好只能用ok了),表示(i)选这四个方向会不会直接与某个点相交。

那么转移时就只需要枚举(i+1)的方向,用(ok)判掉不合法的,再用已知的(j,k,l,m)判掉不合法的就行了。
最终答案就是(sum f_{n,j,k,l,m})

由于状态占用空间较大,我们需要把第一维(i)用滚动数组优化掉,这样空间复杂度就是(O(n^4))
时间复杂度理论上是(O(n^5)),实际上达不到这个上界,因为有大量冗余状态,转移时可以排除掉,问题就解决了。

Code

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;

const int N = 56, P = 998244353;

int n, len, arr[N], ok[N][4], f[2][N][N][N][N];
struct point { int x, y; } p[N];
int cmp(point a, point b) { return a.y == b.y ? a.x < b.x : a.y > b.y; }

void plus(int &a, int b) { a = (a + b) % P; }

int main()
{
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) scanf("%d%d", &p[i].x, &p[i].y);
	sort(p + 1, p + n + 1, cmp);
	for (int i = 1; i <= n; i++) arr[++len] = p[i].x;
	sort(arr + 1, arr + len + 1);
	len = unique(arr + 1, arr + len + 1) - arr - 1;
	for (int i = 1; i <= n; i++) p[i].x = lower_bound(arr + 1, arr + len + 1, p[i].x) - arr;
	for (int i = 1; i <= n; i++)
	{
		for (int j = 1; j < i; j++)
		{
			if (p[i].x == p[j].x) ok[i][0] = 1;
			if (p[i].y == p[j].y)
			{
				if (p[i].x > p[j].x) ok[i][1] = 1;
				if (p[i].x < p[j].x) ok[i][3] = 1;
			}
		}
		for (int j = i + 1; j <= n; j++)
		{
			if (p[i].x == p[j].x) ok[i][2] = 1;
			if (p[i].y == p[j].y)
			{
				if (p[i].x > p[j].x) ok[i][1] = 1;
				if (p[i].x < p[j].x) ok[i][3] = 1;
			}
		}
	}
	f[0][0][len + 1][0][len + 1] = 1;
	for (int i = 0, now = 0, nx; i < n; i++)
	{
		nx = now ^ 1;
		memset(f[nx], 0, sizeof(f[nx]));
		for (int j = 0; j <= len + 1; j++)
			for (int k = 0; k <= len + 1; k++)
				for (int l = 0; l <= len + 1; l++)
					for (int m = 0; m <= len + 1; m++)
						if (f[now][j][k][l][m])
						{
							if (!ok[i + 1][0] && j < p[i + 1].x && k > p[i + 1].x) plus(f[nx][j][k][l][m], f[now][j][k][l][m]);
							if (!ok[i + 1][1] && m > p[i + 1].x) plus(f[nx][max(j, p[i + 1].x)][k][l][m], f[now][j][k][l][m]);
							if (!ok[i + 1][2]) plus(f[nx][j][k][max(l, p[i + 1].x)][min(m, p[i + 1].x)], f[now][j][k][l][m]);
							if (!ok[i + 1][3] && l < p[i + 1].x) plus(f[nx][j][min(k, p[i + 1].x)][l][m], f[now][j][k][l][m]);
						}
		now ^= 1;
	}
	int ans = 0;
	for (int j = 0; j <= len + 1; j++)
		for (int k = 0; k <= len + 1; k++)
			for (int l = 0; l <= len + 1; l++)
				for (int m = 0; m <= len + 1; m++)
					plus(ans, f[n & 1][j][k][l][m]);
	printf("%d
", ans);
	return 0;
}
原文地址:https://www.cnblogs.com/zjlcnblogs/p/10331772.html