• “蔚来杯“2022牛客暑期多校训练营(加赛) D题: Directions


    D题: Directions

    原题链接:https://ac.nowcoder.com/acm/contest/38727/D

    题目大意

    假设地球是完美球体。

    对于球上的两个点 x , y x,y x,y ,定义 w x , y w_{x,y} wx,y 为从 x x x 向西到 y y y 的距离, e x , y e_{x,y} ex,y 为从 x x x 向东到 y y y 的距离。

    w y , x < e y , x w_{y,x}wy,x<ey,x 则称 x x x y y y 的西面。
    w y , x < e y , x w_{y,x}wy,x<ey,x 则称 x x x y y y 的东面。

    定义大小为 n × n n\times n n×n 的方向矩阵 D D D 为:

    • ∀ i ∈ [ 1 , n ] \forall i\in [1,n] i[1,n] ,有 D i , i = ′ ∗ ′ D_{i,i}='*' Di,i=
    • ∀ i , j ∈ [ 1 , n ] , i ≠ j \forall i,j\in [1,n],i\ne j i,j[1,n],i=j ,有 D i , j ∈ W , E D_{i,j}\in{W,E} Di,jW,E

    称方向矩阵 D D D 为真实的,当赤道上存在 n n n 个不同的位置 a 1 , a 2 , . . . , a n a_1,a_2,...,a_n a1,a2,...,an 满足:

    • 从北极上方俯视赤道时,所有点逆时针排列。
    • ∀ i , j ∈ [ 1 , n ] , i ≠ j \forall i,j\in [1,n],i\ne j i,j[1,n],i=j ,如果 D i , j = W D_{i,j}=W Di,j=W a i a_i ai a j a_j aj 西面,反之在东面。

    现在给定某些元素被’?‘表示的方向矩阵 D D D ,每个’?‘可以被替换为’W’或’E’,求有多少真实的方向矩阵可以通过替换’?'得到。

    题解

    假设 a 2 , a 3 , . . . , a p a_2,a_3,...,a_p a2,a3,...,ap a 1 a_1 a1 的东面, a p + 1 , a p + 2 , . . . , a n a_{p+1},a_{p+2},...,a_n ap+1,ap+2,...,an a 1 a_1 a1 的西面。
    a 2 , a 3 , . . . , a p a_2,a_3,...,a_p a2,a3,...,ap 通过球心投影到 a 1 a_1 a1 的东面得到对应点 a 2 ′ , a 3 ′ , . . . , a p ′ a_2',a_3',...,a_p' a2,a3,...,ap
    现在 a i ( 2 ≤ i ≤ p ) a_i(2\le i\le p) ai(2ip) a j ( p + 1 ≤ j ≤ n ) a_j(p+1\le j\le n) aj(p+1jn) 的关系可以对应到 a j a_j aj a i ′ a_i' ai 的关系。
    以下称 x x x y y y 的西面为 x < y xx<y x x x y y y 的东面为 x > y x>y x>y
    那么问题转化为求 a 2 ′ , a 3 ′ , . . . , a p ′ ( a 2 ′ < a 3 ′ < . . . < a p ′ ) a_2',a_3',...,a_p'(a_2'a2,a3,...,ap(a2<a3<...<ap) a p + 1 , a p + 2 , . . . , a n ( a p + 1 < a p + 2 < . . . < a n ) a_{p+1},a_{p+2},...,a_n(a_{p+1}ap+1,ap+2,...,an(ap+1<ap+2<...<an) 合并后可构成的上升序列数。


    下文中的 a i ′ a_i' ai 指第 i i i a ′ a' a (即原来的 a i + 1 ′ a_{i+1}' ai+1 ), a i a_i ai 指第 i i i a a a (即原来的 a p + i a_{p+i} ap+i )
    a ′ a' a 共有 x ( x = p − 1 ) x(x=p-1) x(x=p1) 个, a a a 共有 y ( y = n − p ) y(y=n-p) y(y=np) 个。


    f i , j f_{i,j} fi,j 表示前 i i i a ′ a' a 和前 j j j a a a 可构成的上升序列数。
    每次枚举第个 a i ′ a_i' ai 的位置,可得转移式:
    f i , j = ∑ k = 0 j f i − 1 , k × g i , k f_{i,j}=\sum_{k=0}^jf_{i-1,k}\times g_{i,k} fi,j=k=0jfi1,k×gi,k

    其中 g i , k g_{i,k} gi,k 表示 a i ′ a_i' ai 能否位于 a k a_k ak a k + 1 a_{k+1} ak+1 之间,可得:
    g i , j = ∏ k = 1 j [ a k < a i ′ ] ∏ k = j + 1 y [ a i ′ < a k ] g_{i,j}=\prod_{k=1}^j[a_kgi,j=k=1j[ak<ai]k=j+1y[ai<ak]

    通过预处理前缀和后缀可以 O ( n 2 ) O(n^2) O(n2) 得到所有的 g g g
    但是原先的 d p dp dp 的复杂度是 O ( n 3 ) O(n^3) O(n3) 的(状态 O ( n 2 ) O(n^2) O(n2) ,转移 O ( n ) O(n) O(n) ),如果在此同时枚举 p p p 则总复杂度为 O ( n 4 ) O(n^4) O(n4) ,不能通过。
    再次观察转移式 f i , j = ∑ k = 0 j f i − 1 , k × g i , k f_{i,j}=\sum\limits_{k=0}^jf_{i-1,k}\times g_{i,k} fi,j=k=0jfi1,k×gi,k ,发现 f i , j f_{i,j} fi,j f i , j − 1 f_{i,j-1} fi,j1 的差别只有 f i − 1 , j × g i , j f_{i-1,j}\times g_{i,j} fi1,j×gi,j 一项,因而前缀和优化后可得递推式:
    f i , j = f i , j − 1 + f i − 1 , j × g i , j f_{i,j}=f_{i,j-1}+f_{i-1,j}\times g_{i,j} fi,j=fi,j1+fi1,j×gi,j

    状态 O ( n 2 ) O(n^2) O(n2) ,转移 O ( 1 ) O(1) O(1) d p dp dp 复杂度为 O ( n 2 ) O(n^2) O(n2) ,再算上枚举 p p p O ( n ) O(n) O(n) ,程序总复杂度为 O ( n 3 ) O(n^3) O(n3) ,可以通过。

    注意判断判断初始的矩阵是否合法,同时根据对称将矩阵问号可以直接填充。

    参考代码

    #include
    using namespace std;
    
    template<class T>inline void read(T&x){
    	char c,last=' ';
    	while(!isdigit(c=getchar()))last=c;
    	x=c^48;
    	while(isdigit(c=getchar()))x=(x<<3)+(x<<1)+(c^48);
    	if(last=='-')x=-x;
    }
    
    const int MAXN=5e2+7,P=998244353;
    int n;
    int x,y;
    string s[MAXN];
    int a[MAXN];
    int b[MAXN]; 
    int f[MAXN][MAXN];
    int g[MAXN][MAXN];
    
    int check(){//判断是否合法,并填补已经确定的'?'
    	for(int i=1;i<=n;++i){
    		for(int j=1;j<i;++j){
    			if(s[i][j]==s[j][i]&&s[i][j]!='?')return 1;//W-W或E-E
    			if(s[i][j]!=s[j][i]){//对称填充
    				if(s[i][j]=='W')s[j][i]='E';
    				if(s[i][j]=='E')s[j][i]='W';
    				if(s[j][i]=='W')s[i][j]='E';
    				if(s[j][i]=='E')s[i][j]='W';
    			}
    		}
    	}
    	return 0;
    }
    
    void init(){//预处理g数组
    	static int pre[MAXN][MAXN];
    	static int suf[MAXN][MAXN];
    	for(int i=1;i<=x;++i){
    		pre[i][0]=suf[i][y+1]=1;
    		for(int j=1;j<=y;++j){//预处理前缀(0~y)
    			pre[i][j]=pre[i][j-1]&(s[a[i]][b[j]]!='E');
    		}
    		for(int j=y;j>=1;--j){//预处理后缀(1~y+1)
    			suf[i][j]=suf[i][j+1]&(s[a[i]][b[j]]!='W');
    		}
    		for(int j=0;j<=y;++j)g[i][j]=pre[i][j]&suf[i][j+1];//计算(0~y)
    	}
    }
    
    void work(){
    	init();
    	for(int j=0;j<=y;++j)f[0][j]=1;//dp预处理
    	for(int i=1;i<=x;++i){
    		for(int j=0,sum=0;j<=y;++j){
    			sum=(sum+g[i][j]*f[i-1][j])%P;//前缀和优化(直接使用递推式需要特判下标f[-1])
    			f[i][j]=sum;
    		}
    	}
    }
    
    int main()
    {
    	int T;read(T);
    	while(T--){
    		read(n);
    		for(int i=1;i<=n;++i){
    			cin>>s[i];
    			s[i]=" "+s[i];
    		}
    		if(check()){//原矩阵非法
    			puts("0");
    			continue;
    		}
    		int ans=0;
    		for(int i=1;i<=n;++i){
    			int flag=x=y=0;
    			for(int j=2;j<=i;++j){
    				a[++x]=j;
    				flag|=s[j][1]=='W';
    				for(int k=2;k<j;++k){
    					flag|=s[j][k]=='W';
    				}
    			}
    			for(int j=i+1;j<=n;++j){
    				b[++y]=j;
    				flag|=s[j][1]=='E';
    				for(int k=n;k>j;--k){
    					flag|=s[j][k]=='E';
    				}
    			}
    			if(flag)continue;//划分方案有误
    			work();
    			ans=(ans+f[x][y])%P;
    		}
    		cout<<ans<<'\n';
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
  • 相关阅读:
    消息队列:原理与应用
    矩阵分解算法
    Goland环境配置——Goland上的第一个Go语言程序
    【Struts2】二_Struts2参数映射、核心配置文件struts.xml中的标签与属性的使用
    MySQL数据库JDBC编程
    2000-2018年286个地级市PM2.5数据
    【PAT(甲级)】1067 Sort with Swap(0, i)(附解题思路)
    ELK-日志服务【es-安装使用】
    MoveIt 机械臂运动 学习 01-MoveIt 初次见面
    高校教学教务管理系统文档
  • 原文地址:https://blog.csdn.net/Spy_Savior/article/details/126409455