• 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;
    } 
    
    哈哈哈哈哈哈哈哈哈哈
  • 相关阅读:
    利用Socket.Send发送信息、Socket.SendFile传输文件
    C# Winform局域网传送文件
    Microsoft SQL Server 自定义函数整理大全
    Jquery根据关键字快速定位GridView指定行,用于数据快速检索
    MS SqlServer海量数据分页存储过程收集
    利用tessnet2_32.dll实现OCR验证码识别
    asp.net(C#)常用正则表达式封装
    C# 局域网聊天 发送信息
    Linux/Unix shell 自动发送AWR report
    Oracle数据字典与动态性能视图
  • 原文地址:https://www.cnblogs.com/LZA119/p/13910094.html
Copyright © 2020-2023  润新知