好题。
先考虑给定一个删除的元素集合,怎么判断是否能构造出来。对于一个给定的能构造出某个集合的最短操作序列,我们发现每个元素被删除后就不会再加入了,否则可以得到更短的操作序列。那么对删除的元素集合中,若存在两个数
u
u
u和
v
v
v,且
v
=
u
+
2
v=u+2
v=u+2或
v
=
u
−
K
v=u-K
v=u−K,我们就从
u
u
u往
v
v
v连一条边,可以构造出来当且仅当这样得到的图是DAG。
若
K
K
K为偶数,显然可以分开奇偶考虑,这样合法的条件就是选择的集合中不能有连续的
K
2
\frac{K}{2}
2K个奇数或偶数存在,随便DP一下就好了。
若
K
K
K为奇数,我们将奇数和偶数分别排开,得到了两条链,那么一个不合法的环显然同时存在奇数和偶数。我们容易证明如果存在环,那么一定存在只会切换两次的环(也即
u
−
(
u
+
2
)
−
(
u
+
4
)
−
.
.
.
−
(
u
+
2
t
)
−
(
u
+
2
t
−
K
)
−
(
u
+
2
t
−
K
+
2
)
−
.
.
.
(
u
+
K
)
−
u
u-(u+2)-(u+4)-...-(u+2t)-(u+2t-K)-(u+2t-K+2)-...(u+K)-u
u−(u+2)−(u+4)−...−(u+2t)−(u+2t−K)−(u+2t−K+2)−...(u+K)−u)。这样就容易DP了,我们设
F
[
i
]
[
j
]
[
k
]
F[i][j][k]
F[i][j][k]表示考虑了第一条链的前
i
i
i个数和第二条链的前
i
+
⌊
K
2
⌋
i+\lfloor \frac{K}{2}\rfloor
i+⌊2K⌋个数,第一条链从
i
i
i连续向后取了
j
j
j个数,第二条链从
i
+
⌊
K
2
⌋
i+\lfloor \frac{K}{2}\rfloor
i+⌊2K⌋连续向前取了k个数,转移讨论一下两条链分别下一个取不取即可(只考虑环中有一条经过这两个数的情况)。
时间复杂度
O
(
n
3
)
\mathcal O(n^3)
O(n3)。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int MOD;
inline void add(int &x,int y) {
((x+=y)>=MOD)?x-=MOD:0;
}
int f[2][155][155],g[155][155];
int dp1(int n,int m) {
if (m>n-2) {
int s=1;
for(int i=1;i<=n;i++) s=s*2LL%MOD;
return s;
}
m>>=1;
int u=((n+1)>>1),v=n-u,cur=0;
for(int i=0;i<=u;i++) f[0][i][0]=1;
for(int i=1;i<=m;i++) {
cur^=1;
memset(f[cur],0,sizeof(f[cur]));
for(int j=0;j<=u;j++)
for(int k=0;k<i;k++)
if (f[cur^1][j][k]) {
add(f[cur][j][0],f[cur^1][j][k]);
add(f[cur][j][k+1],f[cur^1][j][k]);
}
}
for(int i=m+1;i<=v;i++) {
cur^=1;
memset(f[cur],0,sizeof(f[cur]));
int r=u-i+m+1;
for(int j=1;j<=r;j++)
for(int k=0;k<i;k++)
if (f[cur^1][j][k]) {
add(f[cur][j-1][0],f[cur^1][j][k]);
if (2*m-k+2>j) add(f[cur][j-1][k+1],f[cur^1][j][k]);
}
for(int j=0;j<r;j++)
for(int k=0;k<i;k++)
if (f[cur^1][0][k]) {
add(f[cur][j][0],f[cur^1][0][k]);
add(f[cur][j][k+1],f[cur^1][0][k]);
}
}
for(int i=v-m+1;i<=u;i++) {
cur^=1;
memset(f[cur],0,sizeof(f[cur]));
int r=u-i+1;
for(int j=1;j<=r;j++)
for(int k=0;k<=v;k++)
if (f[cur^1][j][k]) add(f[cur][j-1][k],f[cur^1][j][k]);
for(int j=0;j<r;j++)
for(int k=0;k<=v;k++)
if (f[cur^1][0][k]) add(f[cur][j][k],f[cur^1][0][k]);
}
int ans=0;
for(int i=0;i<=v;i++) add(ans,f[cur][0][i]);
return ans;
}
int dp2(int n,int m) {
memset(g,0,sizeof(g));
g[0][0]=1;
for(int i=0;i<n;i++)
for(int j=0;j<=i;j++)
if (g[i][j]) {
add(g[i+1][0],g[i][j]);
if (j<m) add(g[i+1][j+1],g[i][j]);
}
int ans=0;
for(int i=0;i<=n;i++) add(ans,g[n][i]);
return ans;
}
int main() {
int n,m;
scanf("%d%d%d",&n,&m,&MOD);
if (m&1) printf("%d\n",dp1(n,m));
else printf("%lld\n",(ll)dp2((n+1)>>1,m>>1)*dp2(n>>1,m>>1)%MOD);
return 0;
}