模拟赛题讲解[31]
来自 yukuai26 2023-08-05 noi.ac #3275
题目描述:
给定 $N$个单调递增的数 $A_i$和 $M$个单调递增的数 $B_i$,从 $A$和 $B$中分别找到一个子序列 $a,b$,使得这两个子序列长度一样并且 $a_i-a_{i+1}=b_i-b_{i+1}$。求这两个子序列的最长长度。
输入格式:
第一行两个整数 $N,M$。
接下来一行 $N$个整数代表 $A$。
接下来一行 $M$个整数代表 $B$。
输出格式:
输出一行一个整数代表答案。
输入样例:
5 5
1 2 3 4 5
4 6 7 8 10
输出样例:
4
数据范围:
对于 $100\%$的数据,$1\leq N,M\leq 1000,1\leq A_i,B_j\leq 10000$。
题解:
居然没想到正解,我真是 stupid 啊。
先讲讲暴力dp解法。设 $f_{i,j}$ 表示考虑到 $a$ 的前 $i$ 个数和 $b$ 的前 $j$ 个数,强制选了 $a_i,b_j$ ,直接看代码:
for(int i = 1; i <= n; i++)
for(int j = 1; j <= n; j++) f[i][j] = 1;
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++)
for(int k = 1; k < i; k++)
for(int l = 1; l < j; l++)
if(a[i] - a[k] == b[j] - b[l]) up(f[i][j], f[k][l] + 1), up(res, f[i][j]);
cout << res << '\n';
开始讲正解。考虑移项,则 $a_i - b_i = a_{i+1}-b_{i+1}$ 。
因此我们只要去出现次数最大 $a_i-b_i$ 的那些 $i$ 就好了
这样为啥对呢,因为都是单调递增的,所以不会有重复的数,就这么水。
时间复杂度 $\mathcal{O}(n + M)$
代码:
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
#define N ((int)(1e3 + 15))
#define M ((int)(2e4 + 55))
int n,m,a[N],b[N],cnt[M];
signed main()
{
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// freopen("check.in","r",stdin);
// freopen("check.out","w",stdout);
cin >> n >> m;
for(int i = 1; i <= n; i++) cin >> a[i];
for(int i = 1; i <= m; i++) cin >> b[i];
for(int i = 1; i <= n; i++)
for(int j = 1; j <= m; j++) ++cnt[a[i] - b[j] + 10000ll];
cout << *max_element(cnt, cnt + 20000 + 30) << '\n';
return 0;
}