[USACO22JAN] Counting Haybales P
[USACO22JAN] Counting Haybales P
原操作等价于交换两堆高度绝对值之差为 \(1\) 的草堆。
如果绝对值之差不为 \(1\)。那么两堆草的相对位置不可能改变。
如果 \(i,j\) 满足 \(i<j\) 且 \(|A_i-A_j|\not=1\)。那么从 \(i\) 向 \(j\) 连一条边。表示 \(i\) 应该在 \(j\) 前面。
原问题就转换为 DAG 拓扑序计数。
这个问题很经典,只能爆搜。
考虑挖掘图的性质,发现所有 \(A_i\) 为奇数的 \(i\) 之间肯定有边。
所以原图可以看成一张以两条链为主干,且两条链之间有边的 DAG。
将两条链看做主干后可以用 dp 来解决问题。设 \(f_{i,j}\) 表示前 \(i\) 个奇数和前 \(j\) 个偶数已经排序了的方案数。
转移的话,如果 \(i+1\) 的所有入边都来自前 \(j\) 个偶数,那么 \(f_{i,j}\) 可以转移到 \(f_{i+1,j}\)。\(j+1\) 同理。
复杂度是 \(O(n^2)\) 的。
代码:
#include<bits/stdc++.h>
#define Mod(x) ((x>=MOD)&&(x-=MOD))
using namespace std;
const int MAXN = 5e3+5;
const int MOD = 1e9+7;
int A[MAXN],n,B[MAXN],C[MAXN];
int f[MAXN][MAXN];
int fst[2][MAXN];
int main()
{
int T;scanf("%d",&T);
while(T--)
{
memset(fst,0,sizeof fst);B[0]=C[0]=0;
for(int i=0;i<=n;++i) for(int j=0;j<=n;++j) f[i][j]=0;
scanf("%d",&n);
for(int i=1;i<=n;++i) scanf("%d",&A[i]);
for(int i=1;i<=n;++i)
{
if(A[i]&1)
{
B[++B[0]]=A[i];
for(int j=C[0];j>=1;--j)
{
if(abs(C[j]-A[i])>1)
{
fst[1][B[0]]=j;
break;
}
}
}
else
{
C[++C[0]]=A[i];
for(int j=B[0];j>=1;--j)
{
if(abs(B[j]-A[i])>1)
{
fst[0][C[0]]=j;
break;
}
}
}
}
f[0][0]=1;
for(int i=0;i<=B[0];++i)
{
for(int j=0;j<=C[0];++j)
{
if(i&&j>=fst[1][i])
{
f[i][j]+=f[i-1][j];
Mod(f[i][j]);
}
if(j&&i>=fst[0][j])
{
f[i][j]+=f[i][j-1];
Mod(f[i][j]);
}
}
}
printf("%lld\n",f[B[0]][C[0]]);
}
return 0;
}
路漫漫其修远兮,吾将上下而求索。