F题解 | #IUPC#

dp题,p = 1<<k, a[t]表示在t时刻可以提交的总题目数。dp[i][j][ij] 表示在时间i时解决j题,在[i-k+1,i]时间内的提交题目的二进制标志是ij。则有

1、ij1 = (ij<<1) % p;

2、 a[i+1] >0 , dp[i][0][0] =1;

3、dp[i+1][j][ij1] = dp[i+1][j][ij1] + dp[i][j][ij];

4、dp[i+1][j+1][ij1+1] = dp[i+1][j+1][ij1+1] + dp[i][j][ij] * (a[i+1]-j)。

时间复杂度O(t * n * 2^k)。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <unordered_map>
#include <vector>
#include <set>
#include <iostream>
typedef long long ll;
using namespace std;
int check(ll x){
    int y=0;
    while(x>0){
        y+=x%2;x=x/2;
    }
    return y;
}
void my_ans(){
    ll x1,x2,y1,y2,i,j,ij,q,n,m,l,r,k,t,x,y,Max = 31,ans=0, mod =1000000007;
    cin>>n>>t>>k>>m;
    int a[t+1];
    memset(a,0,sizeof(a));
    for(i=0;i<n;++i){
        cin>>x;++a[x];
    }
    for(i=1;i<=t;++i)a[i]+=a[i-1];
    y=1<<k;
    ll dp[t+1][n+1][y+1];
    for(i=0;i<=t;++i) for(j=0;j<=n;++j) for(ij=0;ij<=y;++ij) dp[i][j][ij] = 0;
    for(i=0;i<t;++i) {
        if (a[i+1]>0) dp[i][0][0] = 1;
        for(j=0;j<=n;++j) for(ij=0;ij<y;++ij) if (dp[i][j][ij]>0){
            x1 = (ij<<1) % y;
            if (a[i+1]>0 && a[i+1]>=j && check(x1)<=m) dp[i+1][j][x1] = (dp[i+1][j][x1] + dp[i][j][ij]) % mod;
            if (a[i+1] - j>0){
                x1 = x1 +1;
                if (check(x1)<=m)
                    dp[i+1][j+1][x1] = (dp[i+1][j+1][x1] + dp[i][j][ij]*(a[i+1]-j)) % mod;
            }
        }
    }
    
    for(ij=0;ij<y;++ij) ans = (ans+dp[t][n][ij]) % mod;
    cout<<ans<<endl;
    return;
}
int main() {
    ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
    long long t=1,i,j;
    //scanf("%d",&t);
    //cin>>t;
    while(t>0){
        --t;my_ans();
    }
    return 0;
}
// 64 位输出请用 printf("%lld")