P6820 [PA2012] Two Cakes

Description

https://www.luogu.com.cn/problem/P6820

Solution

很好的 dp 题目啊。

首先贪心感觉不能做到全局最优,考虑 dp。

令 $f_{i,j}$ 表示匹配到 $i,j$ 的最小花费。有:

$f_{i,j}\gets\min (f_{i-1,j},f_{i,j-1})+1$

$f_{i,j}\gets\min(f_{i-1,j-1})+1,a_i\not = b_j$。

显然如果能用下面转移是不劣的。而上面转移的次数是 $O(n)$ 的,考虑从这个角度优化。

把两维的 dp 看成在矩形上,对于 $a_i=b_j$ 的格点我们成为特殊点。对于一个,如果反对角线没有其他特殊点那么 $f_{i,j}=\max(i,j)$,否则就从反对角线的上一个特殊点斜着转移过来。

对于非特殊点我们不用去管;对于特殊点 $(i,j)$,我们快速找到 $(i-1,j)$ 和 $(i,j-1)$ 的值,然后取最小。

最后查询 $(n,n)$ 的值即可。

复杂度 $O(n)$。

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
#include<bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define maxn 1000005
#define put() putchar('\n')
#define Tp template<typename T>
#define Ts template<typename T,typename... Ar>
using namespace std;
Tp void read(T &x){
int f=1;x=0;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') {x=x*10+c-'0';c=getchar();}
x*=f;
}
namespace Debug{
Tp void _debug(char* f,T t){cerr<<f<<'='<<t<<endl;}
Ts void _debug(char* f,T x,Ar... y){while(*f!=',') cerr<<*f++;cerr<<'='<<x<<",";_debug(f+1,y...);}
#define gdb(...) _debug((char*)#__VA_ARGS__,__VA_ARGS__)
}using namespace Debug;
#define fi first
#define se second
#define mk make_pair
const int mod=1e9+7;
int power(int x,int y=mod-2) {
int sum=1;
while (y) {
if (y&1) sum=sum*x%mod;
x=x*x%mod;y>>=1;
}
return sum;
}
int n,a[maxn],b[maxn],ans,vis[maxn],f[maxn];
pair<int,int> g[maxn*2];
int calc(int x,int y) {
if (g[x-y+n].fi==0) return max(x,y);
return -g[x-y+n].fi+x+g[x-y+n].se;
}
signed main(void){
int l,r,i;
read(n);
for (i=1;i<=n;i++) read(a[i]);
for (i=1;i<=n;i++) read(b[i]),vis[b[i]]=i;
for (i=1;i<=n;i++) {
f[i]=min(calc(i-1,vis[a[i]]),calc(i,vis[a[i]]-1))+1;
g[i-vis[a[i]]+n]=mk(i,f[i]);
}
printf("%d\n",calc(n,n));
return 0;
}
//i=begin && g++ $i.cpp -o $i -std=c++14 && ./$i