“蔚来杯“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}<e_{y,x} wy,x<ey,x 则称 x x x y y y 的西面。
w y , x < e y , x w_{y,x}<e_{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 x<y x<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'<a_3'<...<a_p') 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}<a_{p+2}<...<a_n) 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_k<a_i']\prod_{k=j+1}^{y}[a_i'<a_k] gi,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<bits/stdc++.h>
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
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值