JZOJ 4228. 【五校联考3day2】C(DP+分类讨论)

JZOJ 4228. 【五校联考3day2】C

题目

Description

在远古的YL国大地上,有 n n n个祭坛,每个祭坛上四个方向写有“艄、毜、鼛、瓯”四个大字,其实这在YL国古代分别是“东、南、西、北”的意思。
YL国每年都要举行祈福消灾的祭祀活动,这个时候,每个祭坛都要在艄毜鼛瓯四个方向中选一个方向,祭坛将向这个方向发出一道隐形的光线,如果两个祭坛发出的光线相交,或者祭坛发出的光线经过了别的祭坛,则祭祀不仅不能成功还将遭到上天的惩罚,如果这些条件都满足则祭祀将成功,YL国在接下来的一年内将不会有任何灾难,甚至只会有人出生不会有人死亡。
抽象的来说,如果我们以“艄”方向为 x x x轴,“瓯”方向为 y y y轴,建立笛卡尔坐标系,那么每个祭坛将会对应一个整点。每个点向上下左右四个方向之一连出一条射线,这些射线不能相交且射线不能经过除了发出点之外的其他点。
现在他们又到了祭祀活动的时候,他们想知道,有多少种方法选择每个祭坛的方向,使得自己的祭祀能成功?输出方案数对 998244353 998244353 998244353取模后的值。

Input

第一行一个正整数 n n n
接下来 n n n行,第 i + 1 i + 1 i+1行两个整数 x i x_i xi, y i y_i yi,表示第i个祭坛在题目中所述的坐标系下的坐标为 ( x i , y i ) (x_i, y_i) (xi,yi)

Output

输出一行一个整数,表示要求的方案数对 998244353 998244353 998244353取模后的值。

Sample Input

输入1:
1
1 1
输入2:
2
1 1
2 2
输入3:
6
0 0
0 1
0 2
0 3
0 4
0 5
输入4:
5
1 3
-4 6
2 4
1 6
5 9
输入5:
10
175470546 566770243
501153312 923840801
-36922529 -888266785
-587403745 908979876
-483726071 -96937502
991096990 -783436017
766700568 -679180551
-601529754 815529478
961445805 303768338
245415738 325847411

Sample Output

输出1:
4
解释:只有一个祭坛,显然四个方向都可以发射。
输出2:
14
解释:
对于所有的 4 × 4 = 16 4 × 4 = 16 4×4=16种情况中,只有两种不可行:
1 1 1号向上, 2 2 2号向左;
1 1 1号向右, 2 2 2号向下。
输出3:
144
解释:
最上面的祭坛可以向左中右三个方向连出射线,最下面的祭坛可以向右下左三个方向连出射线,中间 4 4 4个祭坛可以向左右连出射线,方案数为 3 × 2 × 2 × 2 × 2 × 3 = 144 3 × 2 × 2 × 2 × 2 × 3 = 144 3×2×2×2×2×3=144.
输出4:
117
解释:
祭坛的位置如图所示:
在这里插入图片描述
输出5:
24341

Data Constraint

对于前30%的数据, n ≤ 9 n ≤ 9 n9.
对于前40%的数据, n ≤ 18 n ≤ 18 n18.
对于前60%的数据, n ≤ 36 n ≤ 36 n36.
对于前100%的数据, n ≤ 54 n ≤ 54 n54.
对于所有 i , j i, j i,j,有 x i ≠ x j x_i ≠ x_j xi=xj y i ≠ y j y_i ≠ y_j yi=yj,且 ∣ x i ∣ , ∣ y i ∣ ≤ 1 0 9 |x_i|, |y_i| ≤ 10^ 9 xi,yi109.

题解

  • 题目很好理解,也不难可以简化为:
  • 以给定的 n n n个点为端点,每个点可以往上、下、左、右四个方向之一作射线,使得它们互不相交。求总的方案数。
  • 观察数据限制,发现 n n n很小,
  • 最早想到状压 D P DP DP,很容易发现压不下所有状态。。。
  • 但应该自然想到是一个 D P DP DP题,那应该怎么实现呢?
  • 不妨先把坐标离散化,
  • 再给它们排序,
  • y y y坐标从大到小为第一关键字,以 x x x坐标从小到大为第二关键字,
  • 直观地说就是从上到下,从左到右
  • 先不考虑怎么设状态,分析一下题目条件!!!
  • 按顺序处理每个点,四个方向都要转移,
  • 会有限制条件,想想有哪些限制?
  • 一、经过其他的点

  • 这个很好实现,因为数据很小, n 2 n^2 n2预处理出来是否存在有点在某个点的上、下、左、右即可。
  • 必须先满足条件一不成立,再去判断条件二
  • 二、与其他射线相交

  • 这是这道题目的核心——
  • 1、向下作射线
  • 如图,实心的表示当前做到的点,空心的表示已经做过的点,
  • 因为是从上往下作的,所以上面的无论怎么作也不会受到影响。
    在这里插入图片描述
  • 2、向上作射线
  • 如图,实心的表示当前做到的点,空心的表示已经做过的点,
    在这里插入图片描述
  • 如果存在有一个点,在当前点左边向右作了射线,或,在当前点右边向左作了射线,都不符合条件。
  • 否则就可以向上作射线。
  • 总不能上面一个个点判断吧!
  • 实际上,如果上面最左边的一个向右作的点(重点1)在当前点的右边,最右边一个向左作的点(重点2)在当前点的左边,那就符合条件。
  • 3、向左作射线
  • 如图,实心的表示当前做到的点,空心的表示已经做过的点,
    在这里插入图片描述
  • 如果上面有向下作射线的点在当前点的左边,那就不符合条件。
  • 当然,不能一个个判断。
  • 只要最左边的一个向下作的点(重点3)在当前点的右边,那就符合条件。
  • 4、向右作射线
  • 同3.
  • 只要最右边的一个向下作的点(重点4)在当前点的左边,那就符合条件。
  • ————————————————————————————————————————————————
  • 可以开始设状态了!!!
  • 注意到上面标出的四个重点了吗?用它们来设状态就好了!
  • f [ i ] [ j ] [ k ] [ l ] [ h ] f[i][j][k][l][h] f[i][j][k][l][h]表示当前做到第 i i i个点, j , k , l , h j,k,l,h j,k,l,h分别为四个重点的方案数。
  • 每次枚举状态,去更新其他状态。
  • 假设当前枚举到 i i i,通过 j , k , l , h j,k,l,h j,k,l,h和第 i + 1 i+1 i+1个点的横纵坐标比较,
  • 即可得出当前状态下第 i + 1 i+1 i+1个点是否可以分别往四个方向作射线,
  • 如果可以,记得根据状态转移,转移后的 j , k , l , h j,k,l,h j,k,l,h会有改变:
  • (如当前向下作,且当前点在 l l l的左边,那么就得用 f [ i ] [ j ] [ k ] [ l ] [ h ] f[i][j][k][l][h] f[i][j][k][l][h]更新 f [ i + 1 ] [ j ] [ k ] [ x [ i + 1 ] ] [ h ] f[i+1][j][k][x[i+1]][h] f[i+1][j][k][x[i+1]][h],其他类似)
  • 发现空间不够,那么将第一维设置成 0 / 1 0/1 0/1滚动。

代码

#include<cstdio>
#include<cstring>
using namespace std;
#define md 998244353
struct node
{
	int x,y,p,q;
}a[60];
int xx[56],yy[56],le[56],ri[56],up[56],dn[56];
int f[2][56][56][56][56],g[2][56][56][56][56];
int main()
{
	int n,i,j,k,l,h;
	scanf("%d",&n);
	for(i=1;i<=n;i++) 
	{
		scanf("%d%d",&a[i].p,&a[i].q);
		xx[i]=a[i].p,yy[i]=a[i].q;
	}
	for(i=1;i<n;i++)
		for(j=i+1;j<=n;j++) if(xx[i]>xx[j]) xx[0]=xx[i],xx[i]=xx[j],xx[j]=xx[0];
	for(i=1;i<n;i++)
		for(j=i+1;j<=n;j++) if(yy[i]>yy[j]) yy[0]=yy[i],yy[i]=yy[j],yy[j]=yy[0];
	int ln=0,ln1=0;
	xx[0]=yy[0]=-2147483647;
	for(i=1;i<=n;i++)
	{
		if(xx[i]!=xx[i-1]) ln++;
		for(j=1;j<=n;j++) if(a[j].p==xx[i]) a[j].x=ln;
		if(yy[i]!=yy[i-1]) ln1++;
		for(j=1;j<=n;j++) if(a[j].q==yy[i]) a[j].y=ln1;
	}
	for(i=1;i<n;i++)
		for(j=i+1;j<=n;j++)
			if(a[i].y<a[j].y||(a[i].y==a[j].y&&a[i].x>a[j].x)) a[0]=a[i],a[i]=a[j],a[j]=a[0];
	for(i=1;i<=n;i++)
	{
		dn[i]=up[i]=le[i]=ri[i]=0;
		for(j=1;j<=n;j++) if(a[j].x==a[i].x&&a[j].y<a[i].y) dn[i]=1;
		for(j=1;j<=n;j++) if(a[j].x==a[i].x&&a[j].y>a[i].y) up[i]=1;
		for(j=1;j<=n;j++) if(a[j].y==a[i].y&&a[j].x>a[i].x) ri[i]=1;
		for(j=1;j<=n;j++) if(a[j].y==a[i].y&&a[j].x<a[i].x) le[i]=1;
	}
	memset(g,127,sizeof(g));
	g[0][ln+1][0][ln+1][0]=0;
	f[0][ln+1][0][ln+1][0]=1;
	for(i=0;i<n;i++) 
		for(j=0;j<=ln+1;j++)
			for(k=0;k<=ln+1;k++)
				for(l=0;l<=ln+1;l++)
					for(h=0;h<=ln+1;h++) if(g[i%2][j][k][l][h]==i)
					{
						if(!dn[i+1])//down
						{
							int min=l,max=h;
							if(a[i+1].x<min) min=a[i+1].x;
							if(a[i+1].x>max) max=a[i+1].x;
							if(g[1-i%2][j][k][min][max]!=i+1)
							{
								g[1-i%2][j][k][min][max]=i+1;
								f[1-i%2][j][k][min][max]=f[i%2][j][k][l][h];
							}
							else f[1-i%2][j][k][min][max]+=f[i%2][j][k][l][h],f[1-i%2][j][k][min][max]%=md;
						}
						if(!up[i+1]&&j>a[i+1].x&&k<a[i+1].x)//up
						{
							if(g[1-i%2][j][k][l][h]!=i+1)
							{
								g[1-i%2][j][k][l][h]=i+1;
								f[1-i%2][j][k][l][h]=f[i%2][j][k][l][h];
							}
							else f[1-i%2][j][k][l][h]+=f[i%2][j][k][l][h],f[1-i%2][j][k][l][h]%=md;
						}
						if(!le[i+1]&&l>a[i+1].x)//left
						{
							int max=k;
							if(a[i+1].x>max) max=a[i+1].x;
							if(g[1-i%2][j][max][l][h]!=i+1)
							{
								g[1-i%2][j][max][l][h]=i+1;
								f[1-i%2][j][max][l][h]=f[i%2][j][k][l][h];
							}
							else f[1-i%2][j][max][l][h]+=f[i%2][j][k][l][h],f[1-i%2][j][max][l][h]%=md;
						}
						if(!ri[i+1]&&h<a[i+1].x)//right
						{
							int min=j;
							if(a[i+1].x<min) min=a[i+1].x;
							if(g[1-i%2][min][k][l][h]!=i+1)
							{
								g[1-i%2][min][k][l][h]=i+1;
								f[1-i%2][min][k][l][h]=f[i%2][j][k][l][h];
							}
							else f[1-i%2][min][k][l][h]+=f[i%2][j][k][l][h],f[1-i%2][min][k][l][h]%=md;
						}
					}
	int ans=0;
	for(i=0;i<=ln+1;i++)
		for(j=0;j<=ln+1;j++)
			for(k=0;k<=ln+1;k++)
				for(l=0;l<=ln+1;l++) if(g[n%2][i][j][k][l]==n) ans=(ans+f[n%2][i][j][k][l])%md;
	printf("%d",ans);
	return 0;
} 
posted @ 2019-01-28 20:14  AnAn_119  阅读(73)  评论(0编辑  收藏  举报