[jzoj 4228] 【五校联考3day2】C {DP+离散化}

在古老的YL国,为了确保祭祀活动的成功并避免灾难,必须精确计算祭坛建立射线的方向,确保射线不相交且不经过其他祭坛。通过建立笛卡尔坐标系,每个祭坛对应一个整点,利用动态规划算法,考虑射线的方向限制,求解可能的射线布局方案数量。

题目

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

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

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


解题思路

首先如果有一个点往某一个方向有另一个点,那么这个点就是不可以往这个方向连射线。
然后我们可以将点按x轴从小到大排序处理,这样的我们在DP中只需要处理四个信息:

  1. 往下的y的最大值
  2. 往上的y的最小值
  3. 往右的x的最大值
  4. 往右的x的最小值

代码

#include<cstdio>
#include<algorithm>
#define rep(i,x,y) for (register int i=x;i<=y;i++)
using namespace std;
const int lw=998244353,N=56; 
struct node{int x,y;}a[N];
int f[N][N][N][N][2],ans,n,p,q; 
inline bool cmp(node x,node y){return (x.x==y.x)?(x.y<y.y):(x.x<y.x);}
inline bool check(int i,int k,int j){
    if(j==0&&a[k].x==a[i+1].x&&a[k].y>a[i+1].y) return 1; 
    if(j==1&&a[k].x==a[i+1].x&&a[k].y<a[i+1].y) return 1;
    if(j==2&&a[k].y==a[i+1].y&&a[k].x<a[i+1].x) return 1; 
    if(j==3&&a[k].y==a[i+1].y&&a[k].x>a[i+1].x) return 1; 
    return 0;
}
inline int Min(int x,int y){
	if (!y) return x; else if (!x) return y; 	
	return (a[x].y<a[y].y)?x:y; 
}
inline int Max(int x,int y){
	if (!y) return x; else if (!x) return y; 	
	return (a[x].y>a[y].y)?x:y; 
}
int main(){
	scanf("%d",&n); 
	rep(i,1,n) scanf("%d%d",&a[i].x,&a[i].y); 
	sort(a+1,a+n+1,cmp); f[0][0][0][0][0]=q=1; 
	rep(i,0,n-1) {
		rep(j,0,3){
			bool fl=0; 
			rep(k,1,n) if (check(i,k,j)) {fl=1;break;}
			if (fl) continue; 
			rep(l,0,i) rep(r,0,i) rep(u,0,i) rep(d,0,i) if (f[l][r][u][d][p]){
					if(j==0&&(a[l].y<a[i+1].y||!l))
	  		      	 (f[l][r][Min(i+1,u)][d][q]+=f[l][r][u][d][p])%=lw;
	  		      	if(j==1&&(a[r].y>a[i+1].y||!r))
	  		      	 (f[l][r][u][Max(i+1,d)][q]+=f[l][r][u][d][p])%=lw;
				    if(j==2&&((a[u].y>a[i+1].y&&a[d].y<a[i+1].y)||(!u&&!d)||(a[u].y>a[i+1].y&&!d)||(a[d].y<a[i+1].y&&!u)))
                     (f[l][r][u][d][q]+=f[l][r][u][d][p])%=lw;
                    if(j==3)
                     (f[Max(i+1,l)][Min(i+1,r)][u][d][q]+=f[l][r][u][d][p])%=lw;
			}
		}
		p=(p+1)&1; q=(p+1)&1;
		rep(l,0,n) rep(r,0,n) rep(u,0,n) rep(d,0,n) f[l][r][u][d][q]=0; 
	}
	rep(l,0,n) rep(r,0,n) rep(u,0,n) rep(d,0,n) (ans+=f[l][r][u][d][p])%=lw; 
	printf("%d",ans); 
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值