【AtCoder Regular Contest 065 F】Shuffling

动态规划。

题目大意:

给定 $n,m$,一个 01 串和 $m$ 个区间。

要求依次对串中 $m$ 个区间内的数进行任意重排。

问最终的串有多少种不同的可能。

题解:

我们考虑一位一位确定。

如果有区间 $l,r$,那么我们在决策位置 $i$($i\in[l,r]$) 时,$[i,r]$ 区间内的所有数是可以任意排列的。

我们对每个位置求出这个最右端点。令 $f_{i,j}$ 表示前 $i$ 个位置,当前可以自由使用1 的个数为 $j$ 的方案数。

转移考虑这个位置放 0 还是 1 即可。

时间复杂度 $O(n^2)$。

Code:

#include<cstdio>
const int N=3005,md=1e9+7;
int f[N][N],rg[N],pre[N],n,m;
char ss[N];
inline void upd(int&a){a+=a>>31&md;}
int main(){
    scanf("%d%d%s",&n,&m,ss+1);
    for(int i=1;i<=n;++i)pre[i]=pre[i-1]+ss[i]-48,rg[i]=i;
    for(int i=1;i<=m;++i){
        int l,r;
        scanf("%d%d",&l,&r);
        if(rg[l]<r)rg[l]=r;
    }
    for(int i=2;i<=n+1;++i)if(rg[i-1]>rg[i])rg[i]=rg[i-1];
    f[1][pre[rg[1]]]=1;
    for(int i=1;i<=n;++i)
    for(int _1=0;_1<=n;++_1)
    if(int d=f[i][_1]){
        int s1=pre[rg[i+1]]-pre[rg[i]],_0=rg[i]-i+1-_1;
        if(_1)upd(f[i+1][_1-1+s1]+=d-md);if(_0)upd(f[i+1][_1+s1]+=d-md);
    }
    printf("%d\n",f[n+1][0]);
    return 0;
}