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取模后的值。
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
样例1解释:只有一个祭坛,显然四个方向都可以发射。
输出2:
14
样例2解释:
对于所有的4 × 4 = 16种情况中,只有两种不可行:
1号祭坛向上,2号向左。
1号向右,2号向下
输出3:
144
样例3解释:
最上面的祭坛可以向左中右三个方向连出射线,最下面的祭坛可以向右下左三个方向连出射线,中间4个祭坛可以向左右连出射线,方案数为3 × 2 × 2 × 2 × 2 × 3 = 144。
输出4:
117
样例4解释:
祭坛的位置如图所示:
输出5:
24341
Data Constraint
对于前30%的数据,n ≤ 9。
对于前40%的数据,n ≤ 18。
对于前60%的数据,n ≤ 36。
对于前100%的数据,n ≤ 54,对于所有i, j,有x_i ≠ x_j或y_i ≠ y_j,且|x_i|, |y_i| ≤ {10} ^ 9。
Solution
dp。
首先如果一个点的某一个方向有另一个点,我们肯定不能往这个方向射射线,因为n很小,所以这个可以n^2预处理出来。
接下来我们按x坐标递增的顺序处理点,发现只需要记录4个东西就可以了:
- 前i个点中往上射的点的y坐标的最小值。
- 前i个点中往下射的点的y坐标的最大值。
- 前i个点中往右射的点的y坐标的最小值。
- 前i个点中往右射的点的y坐标的最大值。
所以设f[ i ][ J ][ K ][ L ][ S ]表示第前i个点中第J个点往上射的y坐标最小,第K个点往下射的y坐标最大,第L个点往右射的y坐标最小,第S个点往右射的y坐标最大,(休息一下)的方案数。
接下来考虑如何转移。
首先肯定要枚举i,j,k,l,s
1. 若第i个点往上射。
必须满足第i个点的y坐标大于前面的点中往右射的点的y坐标的最大值。转移时要更新J的值。注意判断没有往右射的点的情况。
2. 若第i个点往下射。
必须满足第i个点的y坐标小于前面的点中往右射的点的y坐标的最小值。转移时更新K的值。注意判断没有往右射的点的情况。
3. 若第i个点往左射。
必须满足第i个点的y坐标在前面往上射的点中y坐标的最小值和往下射的点中y坐标的最大值之间。注意判断没有往右射的上界或下界的情况。
4. 若第i个点往右射。
直接转移。更新L和S。
最后答案即为f[ N ][ 0~N ][ 0~N ][ 0~N ][ 0~N ]的和。但是我们发现这样空间会爆,所以第一维要用滚动。
小优化:若当前f[ i ][ j ][ k ][ l ][ s ]=0则不用转移。会快大概一倍的时间(如果你打的像我一样丑的话)。
Code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define MOD 998244353
#define N 55
using namespace std;
int n,g[N][5],ans;
long long f[2][N][N][N][N];
//往上min up y
//往下max down y
//往右 min right y
//往右 max right y
struct node{int x,y;}a[N];
bool cmp(node i,node j){return i.x<j.x;}
int Min(int x,int y){
if(!x) return y;
if(!y) return x;
if(a[x].y<a[y].y) return x;
return y;
}
int Max(int x,int y){
if(!x) return y;
if(!y) return x;
if(a[x].y>a[y].y) return x;
return y;
}
int main(){
freopen("C.in","r",stdin);
freopen("C.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d%d",&a[i].x,&a[i].y);
}
sort(a+1,a+1+n,cmp);
for(int i=1;i<=n;i++){
g[i][1]=g[i][2]=g[i][3]=g[i][4]=1;//up down left right
for(int j=1;j<=n;j++){
if(i!=j){
if(a[j].x==a[i].x&&a[j].y>a[i].y) g[i][1]=0;
if(a[j].x==a[i].x&&a[j].y<a[i].y) g[i][2]=0;
if(a[j].y==a[i].y&&a[j].x<a[i].x) g[i][3]=0;
if(a[j].y==a[i].y&&a[j].x>a[i].x) g[i][4]=0;
}
}
}
if(g[1][1]) f[1][1][0][0][0]=1;
if(g[1][2]) f[1][0][1][0][0]=1;
if(g[1][3]) f[1][0][0][0][0]=1;
if(g[1][4]) f[1][0][0][1][1]=1;
for(int i=2;i<=n;i++){
for(int j=0;j<=i-1;j++){
for(int k=0;k<=i-1;k++){
for(int l=0;l<=i-1;l++){
for(int s=0;s<=i-1;s++){
if(a[l].y>a[s].y) continue;
f[i&1][j][k][l][s]=0;
if(!f[(i-1)&1][j][k][l][s]) continue;
if(g[i][1]){ //up
if(a[i].y>a[s].y||s==0){
f[i&1][Min(i,j)][k][l][s]+=f[(i-1)&1][j][k][l][s]%=MOD;
}
}
if(g[i][2]){ //down
if(a[i].y<a[l].y||l==0){
f[i&1][j][Max(i,k)][l][s]+=f[(i-1)&1][j][k][l][s]%=MOD;
}
}
if(g[i][3]){ //left
if((k==0||k>0&&a[i].y>a[k].y)&&(j==0||j>0&&a[i].y<a[j].y)){
f[i&1][j][k][l][s]+=f[(i-1)&1][j][k][l][s]%=MOD;
}
}
if(g[i][4]){ //right
f[i&1][j][k][Min(i,l)][Max(i,s)]+=f[(i-1)&1][j][k][l][s]%=MOD;
}
}
}
}
}
}
for(int j=0;j<=n;j++)
for(int k=0;k<=n;k++)
for(int l=0;l<=n;l++)
for(int s=0;s<=n;s++)
ans=(ans+f[n&1][j][k][l][s])%MOD;
printf("%d\n",ans);
return 0;
}
作者:zsjzliziyang
QQ:1634151125
转载及修改请注明
本文地址:https://blog.csdn.net/zsjzliziyang/article/details/86682620