Title

2 つの山札题解

题目链接

题意简述:给定两个长度为 n 的排列 AB,按照一下方式生成一个长度为 2n2 的序列:你对每一个排列分别做 n1 次操作,每一次选择一个序列进行操作,删除该排列的第一个数,将另一个排列第一个数加入序列,问最终能得到多少种序列。

数据范围:1n103

有一个很显然的dp,设 fi,j 表示保留第一个排列中第 in 个元素和第二个排列中第 jn 个元素,进行操作直到两个序列的长度都为 1,能得到的序列数目。

这个dp有一个很naive的转移:fi,j=fi+1,j+fi,j+1,即要么操作序列一,要么操作序列二,倒序枚举计算 f 数组即可。

但是这样转移会算重,我们来考虑如何去重。

首先,只有 Ai=Bj 的时候才会算重,而且 fi.j 多算的部分一定是 fi+1,jfi,j+1 中相同的序列数。

gi,j,k 表示 fi+k,jfi,j+k 中相同的序列数,状态数 n3 级别,显然不行。

但是容易发现,只有当 Ai=Bj 时,gi,j,k 才有意义,又由于序列 AB 都是排列,一个 i 唯一对应一个 j,因此 ij 中只有一个需要表示进状态,即记 gi,k 即可。

考虑转移,首先,gi,kfi+k,jfi,j+k 中的重复序列数,其中可能处于此时排列中的第一个位置的数只有可能是 aibjai+kbj+k,其中可能相等的数对只有 (ai,bj)(ai+k,bj+k)

因此有两种转移,一种是 fi+k,jfi,j+k 分别从 fi+k+1,jfi,j+k+1 转移过来,即 gi,kgi,k+1 ,条件是 ai=bj ,根据 g 的定义,一定满足。

另一种是 fi+k,jfi,j+k 分别从 fi+k,j+1fi+1,j+k 转移过来,条件是 ai+k=bj+k

但是注意,第二种转移不能直接认为是 gi,kgi+1,k1,因为 ai+1bj+1 不一定相等,该转移的正确方式应该为 gi,kgi+k,1k

由于上述转移中用到了 gi,k(k0),因此 k 为非正数的 g 也需要计算,其中 gi,0=fi,jk 为负数的转移和正数一致,但是要注意,gi,k(k<0) 的计算不能在倒序枚举到 i 时计算,而应该在枚举到 i+k 时计算,因为枚举到 i 时,fik,j 还没有被计算,因此此时的 gi,k 还没有意义。

然后 f 的转移就很简单了,fi,j=fi+1,j+fi,j+1[ai==bj]gi,1,初值为 fn,n=1

倒序枚举一下转移 fg 即可,注意 gi,k(k>0)要在 fi,j 之前计算,因为 f 的转移会用到,gi,0 要在 fi,j 之后计算,因为它需要从 fi,j 转移过来。

Code:

#include<bits/stdc++.h>
using namespace std;
const int mod=1000000007;
int n,a[1005],b[1005];
int f[1005][1005];
map<int,int> g[1005];
int id1[1005],id2[1005];
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
		scanf("%d",&a[i]),id1[a[i]]=i;
	for(int i=1;i<=n;i++)
		scanf("%d",&b[i]),id2[b[i]]=i;//记双射,方便查询与 a[i] 相同的 b[j]
	for(int i=n;i>=1;i--)
	{
		int k=id2[a[i]];
		for(int j=n-max(i,k);j>=1;j--)//计算g[i][j](j>0)
		{
			g[i][j]=g[i][j+1];
			if(a[i+j]==b[k+j])
				g[i][j]=(g[i][j]+g[i+j][1-j])%mod;
		}
		for(int j=-1;j>=-n;j--)//计算g[i-j][j](j<0)
		{
			if(i-j>n)
				break;
			int k=id2[a[i-j]];
			g[i-j][j]=g[i-j][j+1];
			if(a[i]==b[k+j])
				g[i-j][j]=(g[i-j][j]+g[i][1-j])%mod;
		}
		for(int j=n;j>=1;j--)//计算f[i][j]
		{
			if(i+j==2*n)
				f[i][j]=1;
			else
			{
				f[i][j]=(f[i+1][j]+f[i][j+1])%mod;
				if(a[i]==b[j])
					f[i][j]=(f[i][j]-g[i][1]+mod)%mod;
		}
		g[i][0]=f[i][k];//计算g[i][0]
	}
	printf("%d\n",f[1][1]);
	
	return 0;
}
posted @   五百年前  阅读(36)  评论(1编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示