zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    BEC listen and translation exercise 44
    中译英12
    BEC listen and translation exercise 43
    中译英11
    BEC listen and translation exercise 42
    中译英10
    BEC listen and translation exercise 41
    中译英9
    BEC listen and translation exercise 40
    中译英8
  • 原文地址:https://www.cnblogs.com/zjlcnblogs/p/10331772.html
Copyright © 2011-2022 走看看