把博客园图标替换成自己的图标
把博客园图标替换成自己的图标end

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();
}
posted @ 2022-10-13 21:09  275307894a  阅读(45)  评论(0编辑  收藏  举报
浏览器标题切换
浏览器标题切换end