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;
    }
    
  • 相关阅读:
    图片灰度化,并且resize图片
    C语言学习笔记
    路飞学城14天集训营作业2—三级菜单
    路飞学城14天集训营作业4—员工信息表
    路飞学城14天集训营作业3—购物车
    路飞学城14天集训营作业1—登陆认证
    js钩子函数
    APP2.0后台控件API
    KindEditor 插件API使用说明
    TreeView插件 API
  • 原文地址:https://www.cnblogs.com/zjlcnblogs/p/10331772.html
Copyright © 2011-2022 走看看