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

题意

在平面直角坐标系上有\(n\)个点,没有重合的点。你要从这\(n\)个点向上下左右四个方向的其中一个引一条射线,最终没有射线相交,求方案数。
\(n\leq 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\n", ans);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/zjlcnblogs/p/10331772.html
今日推荐