[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;
}
posted @ 2022-04-30 15:07  夜空之星  阅读(63)  评论(0编辑  收藏  举报