CF2005E2 Subtangle Game (Hard Version) 题解
不错的 DP 题。
我们先约定一下:以
首先我们可以列出
然后先考虑朴素转移:
首先可以发现如果以
好绕啊!这里有一个形式化一点的表达:
如果存在
然后考虑特殊情况,
首先容易想到的是从边界出发:
-
如果
i=n ,那么取完后后手就没有地方可以操作了,先手必胜。 -
如果
j=m ,那么取完后后手就没有地方可以操作了,先手必胜。 -
如果
k=l ,那么先手取完就没了。
所以如果
然后有一个简单转移:考虑取这里,那么后手拿到的是以
这样子就完成 DP 的转移了,复杂度
然后考虑优化。
提示:考虑分析一下样例,或者再看一眼 DP 的朴素转移,有没有什么规律?
我们可以从 DP 的朴素转移里发现两个性质:
首先,如果
同理,如果
从第一个性质推导,我们惊喜的发现好像可以压维,也就是把
故我们定义
考虑优化转移(如果
提示:当
发现如果
那么,我们肯定取最大的
于是我们定义
那么这个转移为:若
这一点可能有点难,那么讲一下思考过程:
首先我们想让
所以若
然后考虑
首先
然后
最后是朴素转移:
首先
然后是
最后对上述所有值取最大值即可。
然后呢?然后就没了。接下来你只需要写一下求
最后考虑输出:如果先手必胜,那么
时间复杂度:
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1510;
const int mod=998244353;
const int INF=0x3f3f3f3f3f3f3f3f;
int l,n,m;
int a[N];
int b[N][N];
int f[N][N];
int mx[N][N];
int T[N*N];
void solve(){
cin>>l>>n>>m;
for(int i=1;i<=l;i++)cin>>a[i];
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++)
cin>>b[i][j];
for(int i=1;i<=n;i++){//这里开桶记录,如果用map复杂度会多一个log
for(int j=1;j<=m;j++)T[b[i][j]]=j;
for(int j=1;j<=l;j++)mx[i][j]=T[a[j]];
for(int j=1;j<=m;j++)T[b[i][j]]=0;
}
for(int k=l;k>=1;k--){
for(int i=n;i>=1;i--){
f[i][k]=max(f[i][k],f[i+1][k]);
if(mx[i][k]>0&&f[i+1][k+1]<=mx[i][k])f[i][k]=max(f[i][k],mx[i][k]);
if(mx[i][k]>0&&(i==n||k==l))f[i][k]=max(f[i][k],mx[i][k]);
}
}
int fl=0;
if(f[1][1]>0)fl=1;
if(!fl)cout<<"N\n";
else cout<<"T\n";
for(int i=1;i<=n;i++)
for(int j=1;j<=l;j++)
f[i][j]=mx[i][j]=0;
return;
}
signed main(){
ios::sync_with_stdio(0);
cout.tie(0);
cin.tie(0);
int T=1;
cin>>T;
while(T--)solve();
return 0;
}
/*
dp[i][j][k]:考虑(i,j)~(n,m)的子矩阵,a到了第k位,先手是否必胜
考虑初始化:dp[i][j][k]=1(其中b[i][j]=a[k])
考虑转移:
b[i][j]=a[k]:
dp[i][j][k]=(!dp[i+1][j+1][k+1])
i==n or j==m or k==l
b[i][j]!=a[k]:
dp[i][j][k]=dp[x][y][k](i<=x and j<=y)
这是E1的做法
发现转移中,若(i,j)=1,则(i,k)=1 [k<=j]
记:
f[i][k]表示使dp[i][j][k]=1最大的j
mx[i][k]表示使b[i][j]=a[k]最大的j
考虑用这个转移:
f[i][k]=max(mx[i][k],f[i+1][k+1])
i==n or k==l:f[i][k]=mx[i][k]
这是E2的做法
*/