Tutorial for 2020 CCPC Changchun Onsite J (GYM102832J) - Abstract Painting

DP Solutions

Easy to think but hard to write.

Solution #

Since the radius of a circle is at most 5, we only need to check the status of 10 blocks before it, which could be represented as a binary number. Let dpi,jdp_{i, j} be the number of ways to draw circles whose right boundary is ii, with mask of jj. Here if the k-th bit of the mask is 1, it means that you can put a circle whose left boundary is iki-k.

For a fixed right boundary, there are 5 possible positions for center, so 252^5 circle combinations. So our strategy is that for each position, we check 2102^{10} masks and 252^5 circle combinations, then transition if possible.

In order to make coding easier, we could calculate some helper array: le is the mask for the left boundary of the corresponding center mask, all the bits in mhi[i] to the right of the highest bit of le[i] is set to 1 to make positions inside the circle unavailable for the next position.

Code #

Credits

#include <bits/stdc++.h>
 
#define all(x) (x).begin(),(x).end()
#define sz(x) int(x.size())
 
using namespace std;
using ll = long long;
using pii = pair<int, int>;
template<typename... T> void rd(T&... args) {((cin>>args), ...);}
template<typename... T> void wr(T... args) {((cout<<args<<" "), ...);cout<<endl;}
 
constexpr int mod=1e9+7;
ll dp[1010][1<<10];
int already[1010];
int le[32], mhi[32];
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int n, k;
    cin>>n>>k;
    for (int i=0; i<k; i++) {
        int c, r;
        cin>>c>>r;
        already[c+r]|=(1<<(r-1));
    }
 
    for (int i=0; i<32; i++) {
        for (int j=0; j<5; j++) {
            if (i>>j&1) {
                le[i]|=(1<<(2*j+1));
                mhi[i]=(1<<(2*j+1))-1;
            }
        }
    }
    dp[0][0]=1;
    for (int i=0; i<=n; i++) {
        for (int mask=0; mask<1024; mask++) {
            if (!dp[i][mask]) continue;
            for (int k=0; k<32; k++) {
                if ((already[i]&k) != already[i]) continue;
                if ((mask & le[k]) != le[k]) continue;
                int nxt=mask-(mask & mhi[k]);
                nxt=(2*nxt+1)&1023;
                (dp[i+1][nxt]+=dp[i][mask])%=mod;
            }
        }
 
    }
    ll ans=0;
    for (int i=0; i<1024; i++) (ans+=dp[n+1][i])%=mod;
    cout<<ans;
    return 0;
}