【CP】ARC065'F シャッフル (Shuffle)
虽然 AT 有 \(2600\) 但是现在再回来看好像每一步都很简单。
数据范围启示我们写一个 \(\mathcal O(n^2)\) 的算法。如果可以完全 shuffle,那么显然是一个 \(C_{i,j}=C_{i-1,j}+C_{i-1,j-1}\) 的组合数,但是这里的 shuffle 加上了一些限制。
考虑沿用组合数的递推式:\(dp_{i,j}\) 为前面 \(i\) 位 \(j\) 个 \(1\) 的方案数,而我们要做的就是钦定能够被转移到的区间的上下界。
因为 \(l_i\) 单调不减,所以显然可以得到每个元素向右所能走到最远的地方 \(R_i\)。在可控范围内使得 \(1\) 最多或者最少就是上下界,这显然是贪心问题。
具体的,上界就是把 \(1\) 往前放,因此上界为 \([1,R_i]\) 内 \(1\) 的数量。
下界是把 \(1\) 往后放,因此下界为 \(i\) 除去 \([1,R_i]\) 内 \(0\) 的数量。
因为可能有太多 \(1\) 或者 \(0\),所以记得与 \([0,i]\) 取交集。
注意不合法状态和边界状态和组合数处理都是同理的。
- \(dp_{i,0}=dp_{i-1,0}\)。
- \(dp_{0,0}=1\)。
- 不合法状态为 \(0\)。
顺带一提,官方题解使用了区间 dp 的理解方式,然后因为 \(L\) 和 \(R\) 相关把其中的一维优化掉了。看看这么想会不会帮助你理解这道题。
#include<bits/stdc++.h>
#define int long long
#define rep(i,l,r) for(int i = l; i <= r; i++)
using namespace std;
const int MX = 3005;
int a[MX];
int MR[MX];
int dp[MX][MX];
const int P = 1e9 + 7;
signed main() {
    int N, M;
    cin >> N >> M;
    rep(i, 1, N) {
        MR[i] = i;
        char c;
        cin >> c;
        a[i] = a[i - 1] + (c - '0');
    }
    rep(i, 1, M) {
        int L, R;
        cin >> L >> R;
        MR[L] = max(MR[L], R);
    }
    rep(i, 1, N) {
        MR[i] = max(MR[i - 1], MR[i]);
    }
    dp[0][0] = 1;
    rep(i, 1, N) {
        int L = max(0ll, i - MR[i] + a[MR[i]]), R = min(i, a[MR[i]]);
        rep(j, L, R)
            dp[i][j] = (dp[i - 1][j] + (j ? dp[i - 1][j - 1] : 0)) % P;
    }
    cout << dp[N][a[N]] << endl;
	return 0;
}
 
                    
                     
                    
                 
                    
                
 
                
            
         
         浙公网安备 33010602011771号
浙公网安备 33010602011771号