最长公共子序列

也许更好的阅读体验

D e s c r i p t i o n \mathcal{Description} Description

对于一个长度为 A A A的正整数序列 B B B,定义其一个长度为 C ( 0 < C ≤ A ) C\left(0 < C \leq A\right) C(0<CA)的非空子
序列为一个长度为 C C C的下标序列 P 1... C P_{1 ... C} P1...C,满足 1 ≤ P 1 < P 2 < ⋯ < P C ≤ A 1 \leq P_1 < P_2 < ⋯ < P_C \leq A 1P1<P2<<PCA
子序列本身就是按照顺序把对应的元素拿出来 : B P 1 , B P 2 , . . . B P C :B_{P_1},B_{P_2}, ... B_{P_C} :BP1,BP2,...BPC
给定一个长度为 N N N的正整数序列 S S S和一个长度为 M M M的正整数序列 T T T,同时再给
定一个有 K K K条边的有向图 G G G。请你求出它们有多少个公共子序列,满足把子序列
拿出来之后,对于任意相邻两个元素 ( a , b ) (a, b) (a,b),满足在 G G G中存在一条有向边 < a , b > < a, b > <a,b>
这里子序列不同,定义为下标位置不同(即序列 P P P不同)。

1 ≤ N , M ≤ 3000 , 0 ≤ K ≤ 1 0 6 , ∀ 1 ≤ i ≤ N , 1 ≤ S [ i ] ≤ 1 0 9 , ∀ 1 ≤ i ≤ M , 1 ≤ T [ i ] ≤ 1 0 9 , 1 ≤ s , t ≤ 1 0 9 1 ≤ N, M ≤ 3000 ,0 ≤ K ≤ 10^6 ,∀1 ≤ i ≤ N, 1 ≤ S[i] ≤ 10^9, ∀1 ≤ i ≤ M, 1 ≤ T[i] ≤ 10^9, 1 ≤ s, t ≤ 10^9 1N,M3000,0K106,1iN,1S[i]109,1iM,1T[i]109,1s,t109

S o l u t i o n \mathcal{Solution} Solution

考虑和求最长公共子序列类似的dp方式,先固定一个公共子序列的末端为ai,然后枚举bj 找到bj时将前面允许的合法转移加上即可
考试时脑子抽了,打了个树状数组,枚举边,但是ai被固定只需用邻接表查看即可

C o d e \mathcal{Code} Code

/*******************************
Author:Morning_Glory
LANG:C++
Created Time:2019年10月09日 星期三 08时13分00秒
*******************************/
#include <cstdio>
#include <fstream>
#include <algorithm>
using namespace std;
const int maxn = 6005;
const int mod = 1000000007;
//{{{cin
struct IO{
	template<typename T>
	IO & operator>>(T&res){
		res=0;
		bool flag=false;
		char ch;
		while((ch=getchar())>'9'||ch<'0')	flag|=ch=='-';
		while(ch>='0'&&ch<='9')	res=(res<<1)+(res<<3)+(ch^'0'),ch=getchar();
		if (flag)	res=~res+1;
		return *this;
	}
}cin;
//}}}
int n,m,k,tot,ans;
int a[maxn],b[maxn],c[maxn],f[maxn];
bool mp[maxn][maxn];
inline void add (int &x,int y){	x=(x+y)%mod;}
inline int lsh (int x) { return lower_bound(c+1,c+tot+1,x)-c; }
int main()
{
	/*
	 * 考虑和求最长公共子序列类似的dp方式,先固定一个公共子序列的末端为ai,然后枚举bj 找到bj时将前面允许的合法转移加上即可
	 * 考试时脑子抽了,打了个树状数组,枚举边,但是ai被固定只需用邻接表查看即可
	 */
	cin>>n>>m>>k;
	for (int i=1;i<=n;++i)	cin>>a[i],c[++tot]=a[i];
	for (int i=1;i<=m;++i)	cin>>b[i],c[++tot]=b[i];

	sort(c+1,c+tot+1);
	tot=unique(c+1,c+tot+1)-c-1;
	for (int i=1;i<=n;++i)	a[i]=lsh(a[i]);
	for (int i=1;i<=m;++i)	b[i]=lsh(b[i]);

	for (int i=1;i<=k;++i){
		int u,v,a,b;
		cin>>a>>b;
		u=lsh(a),v=lsh(b);
		if (u<=tot&&v<=tot&&c[u]==a&&c[v]==b)	mp[u][v]=true;
	}

	for (int i=1;i<=n;++i){
		int s=1;
		for (int j=1;j<=m;++j){
			int t=f[j];
			if (a[i]==b[j])	add(f[j],s),add(ans,s);
			if (mp[b[j]][a[i]])	add(s,t);
		}
	}
	printf("%d\n",ans);
	return 0;
}

如有哪里讲得不是很明白或是有错误,欢迎指正
如您喜欢的话不妨点个赞收藏一下吧

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值