luogu P8100 [USACO22JAN] Counting Haybales P
总是不太会做这种一步的dp,每次都要想很长时间。
首先题面中这个操作可以理解为交换,而一个数在目标序列中的某个位置当且仅当这个数能够通过一系列绝对值为\(1\)的交换换到这个位置上去。
但是这样子不好dp,再对其奇偶染色,可以发现每次交换的是异色的点,这样的话同色的点的相对位置不变。
因此可以设\(dp_{i,j}\)表示黑色放到第\(i\)个,白色放到第\(j\)个的方案数,那么接下来放的一个数要满足这个数到异种数的一段内都能放。可以\(O(1)\)转移。
总复杂度\(O(n^2)\)
code:
#include<bits/stdc++.h>
#define Gc() getchar()
#define Me(x,y) memset(x,y,sizeof(x))
#define Mc(x,y) memcpy(x,y,sizeof(x))
#define d(x,y) ((m)*(x-1)+(y))
#define R(n) (rnd()%(n)+1)
#define Pc(x) putchar(x)
#define LB lower_bound
#define UB upper_bound
#define PB push_back
using ll=long long;using db=double;using lb=long db;using ui=unsigned;using ull=unsigned ll;
using namespace std;const int N=5e3+5,M=1e4+5,K=2e3+5,mod=1e9+7,Mod=mod-1;const db eps=1e-5;const int INF=1e9+7;
int n,m,k,x,y,z,id[N],C1,C2,L[N],R[N],A[N],p1[N],p2[N];ll dp[N][N];
void Solve(){
int i,j;scanf("%d",&n);C1=C2=0;for(i=1;i<=n;i++) scanf("%d",&A[i]),A[i]&1?(p2[id[i]=++C2]=i):(p1[id[i]=++C1]=i);Me(L,-1);Me(R,0);
for(i=1;i<=n;i++){R[i]=(A[i]&1?C1+1:C2+1);for(j=i;j;j--) if((A[i]&1)^(A[j]&1)&&abs(A[i]-A[j])>=2) {L[i]=id[j];break;}for(j=i;j<=n;j++) if((A[i]&1)^(A[j]&1)&&abs(A[i]-A[j])>=2) {R[i]=id[j];break;}}
for(i=0;i<=C1;i++) for(j=0;j<=C2;j++) dp[i][j]=0;dp[0][0]=1;for(i=0;i<=C1;i++) for(j=0;j<=C2;j++){
if(i&&j>=L[p1[i]]&&j<R[p1[i]]) dp[i][j]=(dp[i][j]+dp[i-1][j]);if(j&&i>=L[p2[j]]&&i<R[p2[j]]) dp[i][j]=(dp[i][j]+dp[i][j-1])%mod;
}printf("%lld\n",dp[C1][C2]);
}
int main(){
freopen("1.in","r",stdin);
int T;scanf("%d",&T);while(T--) Solve();
}