ZJOI是什么神仙出题组,能想到这道题目,不看题解一辈子写不出来
这道题不可做,不看题解连思路都没有(我还是太蒟蒻了)
首先设立状态转移数组L[l][r],表示在[l,r]的左边放上一段大小为L[l][r]的石子的情况下必败,同理可以定义R[l][r]
然后也可以很快的得到边界情况,就是L[i][i]=R[i][i]=w[i],这个也很好理解对于两堆相同的石子,后手一定可以进行和先手对称的操作
再然后好像大概是没有思路了(我就止步于此),然后呢,很简单(很简单在哪里),暴力分讨就行了
我们在此先分讨L[l][r],R[l][r]也可以同理得到
首先令L=L[l][r−1],R=R[l][r−1],x=w[r]
Case1
R=x时由定义可得此时[l,r]是必败局面,L[l][r]=0
Case2
x<min(L,R)时L[l][r]=x,即证状态(x,al,al+1...ar−1,x)是必败态
因为左右两端相等,所以后手一定能进行和先手对称的操作,直到产生形如
(al,al+1...ar−1,y)或(y,al,al+1...ar−1)的状态
因为0<y≤x<min(L,R),由数组定义可得此时L=R=y一定是必胜态,即先手必败
注意这里我们规定了x=0,因此后面如果要直接使用Case2需要满足x=0这个条件
Case3
L≤x<R时L[l][r]=x+1,即证状态(x+1,al,al+1...ar−1,x)是必败态
这里继续分讨
A.若先手拿左边的那堆,设拿完后还剩z个
1.若z>L,后手可以将右边那堆拿成z−1个,因为z−1≥L>0,回到Case3本身
2.若z=L,后手不妨直接将右边那堆拿完,由L[l][r−1]定义可知目前是必败局面
3.若0<z<L,后手可以将右边那堆拿成z个,就回到Case2是必败局面
4.若z=0,此时右边那堆的个数L≤k<R,由定义可得目前是必败局面
B.若先手拿右边的那堆,设拿完后还剩z个
1.若z>L,后手可以将左边那堆拿成z+1个,回到Case3本身
2.若0<z≤L,后手可以将最左边那堆拿成z个,回到Case2的必败局面
3.若z=0,后手可以将最左边那堆拿成L个,由定义可得目前是必败局面
Case4
R<x≤L时L[l][r]=x−1,即证状态(x−1,al,al+1...ar−1,x)是必败态
证明同Case3
A.若先手拿左边的那堆,设拿完后还剩z个
1.若z≥L,后手可以将右边那堆拿成z+1个,回到Case4本身
2.若0<z<L,后手可以将最右边那堆拿成z个,回到Case2的必败局面
3.若z=0,后手可以将最右边那堆拿成R个,由定义可得目前是必败局面
B.若先手拿右边的那堆,设拿完后还剩z个
1.若z≥R,后手可以将左边那堆拿成z−1个,回到Case4本身
2.若z=R,后手不妨直接将左边那堆拿完,由R[l][r−1]定义可知目前是必败局面
3.若0<z<R,后手可以将左边那堆拿成z个,就回到Case2是必败局面
4.若z=0,此时左边那堆的个数0<k<L,由定义可得目前是必败局面
Case5
x>max(L,R)时L[l][r]=x,即证状态(x,al,al+1...ar−1,x)是必败态
假设先手取了其中一堆变成了z个
1.若z>max(L,R),回到Case5本身
2.若0<z<max(L,R),将另一堆也拿成z个便得到Case2的必败局面
3.若z=0,只需要将另一堆拿成L或R,由定义可得此时必败
4.若L≤z≤R或R≤z≤L
Case3可以解决最左堆L<z≤R,最右堆L≤z<R的情况
同理Case4可以解决最左堆R≤z<L,最右堆R<z≤L的情况
所以我们只需解决最左堆是z=L和最右堆是z=R的情况
而这两种情况把另一堆拿完就可以由定义得到必败
综上所述,我们得到
L[l][r]=
\begin{cases}
0, &{x=R}\\
x+1, &{L\le x\lt R}\\
x-1, &{R\lt x\le L}\\
x, &otherwise
\end{cases}$$
<h4>
同理也可以得到$R[l][r]$的转移方式
而最后解的判断直接由数组定义得到
当且仅当$L[2][n]=w[1]$或$R[1][n-1]=w[n]$时(只用判断一种)时先手必败
</h4>
```c++
/*
其实也挺好想的一道题目,每次的子游戏只有少了最左和最右两种
然后带记忆化 DFS 一下应该就可以解决了
其实也可以类似区间 DP 的思路找吧
私人阴间分讨,ZJOI你无敌了 🐎爆炸了
*/
#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define pii pair< int,int >
#define QAQ return ;
#define QWQ return 0;
#define TAT return
const int N=1e3+10;
int w[N];
int L[N][N] ,R[N][N];
void sovel()
{
int n;
cin>>n;
for( int i=1;i<=n;i++ )
for( int j=1;j<=n;j++ )
L[i][j]=R[i][j]=0;
for( int i=1;i<=n;i++ ) cin>>w[i], L[i][i]=R[i][i]=w[i];
for( int len=2;len<=n;len++ )
{
for( int l=1, r=l+len-1;r<=n;l++, r++ )
{
int l1=L[l][r-1], r1=R[l][r-1], x1=w[r];
if( x1 == r1 ) L[l][r]=0;
else if( x1 >= l1 && x1 < r1 ) L[l][r]=x1+1;
else if( x1 <= l1 && x1 > r1 ) L[l][r]=x1-1;
else L[l][r]=x1;
int l2=L[l+1][r], r2=R[l+1][r], x2=w[l];
if( x2 == l2 ) R[l][r]=0;
else if( x2 >= r2 && x2 < l2 ) R[l][r]=x2+1;
else if( x2 <= r2 && x2 > l2 ) R[l][r]=x2-1;
else R[l][r]=x2;
}
}
cout<<( L[2][n] == w[1] ? "0\n" : "1\n" );
}
int main()
{
// freopen( "txt.in","r",stdin );
ios::sync_with_stdio( false );
cin.tie( nullptr ), cout.tie( nullptr );
int t;
for( cin>>t;t;t-- ) sovel();
QWQ
}
```