SRM601

原创 2018年04月15日 09:28:24

500pts:
首先有个暴力dpdp[i][a][b],到了第i个数,异或和分别为ab
考虑优化,我们发现第二个比第一个大肯定是在某一位变化,那么前面的所有位都相同,后面无需考虑。
dp[i][a][b]表示到了第i个数,两个集合异或和为a,第一个集合不同位是b
实现时候有一些小技巧,比如说忽略后面的位。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 2065, P = 1e9 + 7;
int n, m, ans, p;
int dp[maxn][maxn][2];
int dfs(int N, int mask, int b) {
    if(N == 0) {
        return mask == 1 && !b;
    }
    if(dp[N][mask][b] != -1) {
        return dp[N][mask][b];
    }
    int ret = 0;
    if(N <= n) {
        ret = (ret + dfs(N - 1, mask ^ (N >> p), b ^ (N >> p & 1))) % P;
    }
    if(N <= m) {
        ret = (ret + dfs(N - 1, mask ^ (N >> p), b)) % P;
    }
    ret = (ret + (dfs(N - 1, mask, b))) % P;
    return dp[N][mask][b] = ret;
}
class WinterAndSnowmen {
    public:
        int getNumber(int N, int M) {
            n = N;
            m = M;
            for(p = 0; p < 11; ++p) {
                memset(dp, -1, sizeof(dp));
                ans = (ans + dfs(max(n, m), 0, 0)) % P;
            }
            return ans;
        }
};

1000pts:
暴力dpdp[i][a][b][c][d],表示第i天,第一个人用了ab,第二个人用了cd。考虑优化,如果所有时间都只有一个人,那么dp[i][a][b]即可。
现在考虑重叠的时间,这段时间可以通过组合数计算,然后每次跳过重叠的时间即可。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <vector>
using namespace std;
typedef long long ll;
const int maxn = 105, P = 1e9 + 7;
struct node {
    int f, r, g, b;
    int end() {
        return f + r + g + b;
    }
};
int n;
vector<node> s[505];
int f[maxn], r[maxn], g[maxn], b[maxn];
ll dp[505][maxn][maxn], c[505][505];
ll C(int A, int B, int C) {
    return c[A + B + C][A] * c[B + C][B] % P;
}
ll dfs(int i, int r, int g) {
    if(i == 501) {
        return !r && !g;
    }
    if(dp[i][r][g] != -1) {
        return dp[i][r][g];
    }
    ll ret = 0;
    if(s[i].size() == 0) {
        ret = dfs(i + 1, 0, 0);
    } else if(s[i].size() == 1) {
        if(i + 1 == s[i][0].end()) {
            ret = dfs(i + 1, 0, 0);
        } else {
            if(r + 1 <= s[i][0].r) {
                ret += dfs(i + 1, r + 1, g);
            }
            if(g + 1 <= s[i][0].g) {
                ret += dfs(i + 1, r, g + 1);
            }
            if(i - s[i][0].f - r - g + 1 <= s[i][0].b) {
                ret += dfs(i + 1, r, g);
            }
            ret %= P;
        }
    } else {
        node s0 = s[i][0], s1 = s[i][1];
        int b = i - s0.f - r - g;
        if(s0.end() == s1.end()) {
            if(s0.r - s1.r == r && s0.g - s1.g == g && s0.b - s1.b == b) {
                ret = C(s1.r, s1.g, s1.b) * dfs(s0.end(), 0, 0) % P;
            }
        } else if(s0.end() < s1.end()) {
            if(s0.r - r <= s1.r && s0.g - g <= s1.g && s0.b - b <= s1.b) {
                ret = C(s0.r - r, s0.g - g, s0.b - b) * dfs(s0.end(), s0.r - r, s0.g - g) % P;
            }
        } else {
            if(s0.r - r - s1.r >= 0 && s0.g - g - s1.g >= 0 && s0.b - b - s1.b >= 0) {
                ret = C(s1.r, s1.g, s1.b) * dfs(s1.end(), r + s1.r, g + s1.g) % P;
            }
        }
    }
    return dp[i][r][g] = ret;
}
int main() {
    scanf("%d", &n);
    for(int i = 1; i <= n; ++i) {
        scanf("%d", &f[i]);
    }
    for(int i = 1; i <= n; ++i) {
        scanf("%d", &r[i]);
    }
    for(int i = 1; i <= n; ++i) {
        scanf("%d", &g[i]);
    }
    for(int i = 1; i <= n; ++i) {
        scanf("%d", &b[i]);
    }
    for(int i = 1; i <= n; ++i) {
        for(int j = f[i]; j < f[i] + r[i] + g[i] + b[i]; ++j) {
            s[j].push_back((node){f[i], r[i], g[i], b[i]});
        }
    }
    c[0][0] = 1;
    for(int i = 1; i <= 500; ++i) {
        c[i][0] = 1;
        for(int j = 1; j <= i; ++j) {
            c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % P;
        }
    }
    for(int i = 0; i <= 500; ++i) {
        if(s[i].size() == 2) {
            if(s[i][0].f > s[i][1].f) {
                swap(s[i][0], s[i][1]);
            }
        }
    }
    memset(dp, -1, sizeof(dp));
    printf("%lld\n", dfs(0, 0, 0));
    return 0;
}
收藏助手
不良信息举报
您举报文章:SRM601
举报原因:
原因补充:

(最多只允许输入30个字)