[DP记录]AT2161 [ARC065D] シャッフル / Shuffling

command_block

2021-05-02 21:09:35

Personal

**题意** : 给出一个长度为 $n$ 的 $01$ 串 $S$ 。 给出 $m$ 个区间,第 $i$ 个区间为 $[l_i,r_i]$ ,顺次进行下列操作 : - 任意地排列 $S$ 的第 $l_i$ 到第 $r_i$ 个字符。 保证 $l_i$ **不降**。 请你求出在 $m$ 次操作后,可以得到多少种不同的 $01$ 串。答案对 $10^9+7$ 取模。 $n,m\leq 3000$ ,时限$\texttt{2s}$。 ------------ 若两个区间 $[l_i,r_i],[l_j,r_j]$ 满足 $l_i\leq l_j,r_j\leq r_i$ ,则可以丢弃 $[l_j,r_j]$。这样, $r_i$ 也是不降的。 不难发现,由于 $l_i$ 不降,在执行完第 $i$ 次操作后,范围 $[1,l_{i+1})$ 就已经确定了。 第 $i$ 次操作只会确定 $[l_i,\min(l_{i+1}-1,r_i)]$ 这一段。 考虑 $\rm DP$ : 设 $dp[i][j]$ 表示处理了前 $i$ 个区间,$[1,l_{i+1})$ 内有 $j$ 个 $1$ 的方案数。 记 $c[i]$ 为 $S[1,i]$ 中 $1$ 的个数。 边界 : $dp[0][c[l_1-1]]=1$。 转移 : - $r_i<l_{i+1}$ $[l_i,r_i]$ 内 $1$ 的个数必为 $c[r_i]-j$。 $$dp[i+1][c[l_{i+1}-1]]\leftarrow dp[i][j]\dbinom{r_i-l_i+1}{c[r_i]-j}$$ - $r_i\geq l_{i+1}$ $[l_i,l_{i+1})$ 内 $1$ 的个数至多为 $\min\{c[r_i]-j,l_{i+1}-l_{i}\}$,至少为 $\max\big\{c[r_i]-j-(r_i-l_{i+1}+1),0\big\}$ 记上下界分别为 $L,R$ ,则对于 $k\in[L,R]$ : $$dp[i+1][j+k]= dp[i][j]\dbinom{r_i-l_i+1}{k}$$ $k$ 的范围大小上界为 $[l_{i+1}-l_{i}]$ ,故总复杂度为 $O(n^2)$。 答案为 $dp[m][c[n]]$。 ```cpp #include<algorithm> #include<cstdio> #define ll long long #define MaxN 3050 using namespace std; const int mod=1000000007; ll powM(ll a,int t=mod-2){ ll ret=1; while(t){ if (t&1)ret=ret*a%mod; a=a*a%mod;t>>=1; }return ret; } ll fac[MaxN],ifac[MaxN]; ll C(int n,int m) {return fac[n]*ifac[m]%mod*ifac[n-m]%mod;} void Init(int n) { fac[0]=1; for (int i=1;i<=n;i++) fac[i]=fac[i-1]*i%mod; ifac[n]=powM(fac[n]); for (int i=n;i;i--) ifac[i-1]=ifac[i]*i%mod; } int n,m,m2,l[MaxN],r[MaxN],c[MaxN],dp[MaxN][MaxN]; char s[MaxN]; int main() { scanf("%d%d%s",&n,&m,s+1);Init(n); for (int i=1;i<=n;i++)c[i]=c[i-1]+(s[i]=='1'); for (int t=1,tl,tr;t<=m;t++){ scanf("%d%d",&tl,&tr); if (tr>r[m2]){ if(l[m2]==tl)m2--; l[++m2]=tl;r[m2]=tr; } }l[m2+1]=n+1; dp[0][c[l[1]-1]]=1; for (int i=1;i<=m2;i++) if (r[i]<l[i+1]){ for (int j=0;j<=c[r[i]];j++) dp[i][c[l[i+1]-1]]=(dp[i][c[l[i+1]-1]]+dp[i-1][j]*C(r[i]-l[i]+1,c[r[i]]-j))%mod; }else { for (int j=0;j<=n;j++)if (dp[i-1][j]){ int R=min(c[r[i]]-j,l[i+1]-l[i]),L=max(c[r[i]]-j-(r[i]-l[i+1]+1),0); for (int k=L;k<=R;k++) dp[i][j+k]=(dp[i][j+k]+dp[i-1][j]*C(l[i+1]-l[i],k))%mod; } } printf("%d",dp[m2][c[n]]); return 0; } ```