[十二省联考2019]皮配 题解

题目:[十二省联考2019]皮配
设S等于各学校人数之和。
首先,有一个很简单的\(O(nM^2)\)\(dp\):记录当前考虑到哪所学校,以及蓝阵营人数a,鸭派系人数b,最后,只要满足\(S-C1<=a<=C0\)\(S-D1<=b<=D0\),这个方案就是合法的。
写上这个就有50分了。
注意到还有20分满足\(k=0\),即没有限制,考虑这部分的做法:
我们发现,对于一个城市,无论选择哪个阵营,其中的学校都可以去鸭派系或 R 派系。
换句话说,阵营与派系互不影响。
所以,可以对阵营与派系分别\(dp\),然后乘起来即可。时间复杂度为\(O(nM)\)
这样就有70分了。然而考场上写错了这20分,只有50。
考虑100分做法:
我们发现:k的范围很小(只有30),这意味着有很多城市的所有学校都是没有限制的。
所以,对于这些城市,我们可以使用\(k=0\)的方法计算。这部分时间复杂度为\(O(nM)\)
考虑其余的城市:
因为一个城市可能有很多学校,所以这些城市可能有很多学校,不能暴力。
但是,如果一个学校没有限制,那么选择派系的情况不受阵营干扰。
所以,我们拿出这些城市中没有限制的学校,只对b(派系)做dp。这部分时间复杂度为\(O(nM)\)
再考虑有限制的学校:
首先,这些学校选择派系的情况阵营干扰,所以不能用之前的方法dp。
但是,这些学校只有k个,可以使用50分的暴力dp。这部分时间复杂度为\(O(kM^2)\),使用滚动数组,卡卡常就不会超时。
在这次dp中,就把上次dp的结果考虑进去,相当于合并背包。
最后\(O(M^2)\)合并背包即可。总时间复杂度为\(O(kM^2)\)
注意细节,注意卡常。
代码:

#include <stdio.h>
#include <vector>
#define md 998244353
#define max(a,b) (a>b?a:b)
#define min(a,b) (a<b?a:b)
#define re register
#define getsua(l,r) (l>0?(sua[r]-sua[l-1]+md)%md:sua[r])
#define getsub(l,r) (l>0?(sub[r]-sub[l-1]+md)%md:sub[r])
using namespace std;
int f[1010][2510],he[2510],sz[2510],ty[2510],wz[2510];
int ta[2510],tb[2510],tc[2510],ts[2510];
int g[2510][2510][2],sua[2510],sub[2510],c0,c1,d0,d1;
bool bk[2510],jd[2510];
bool ya[2][4]={0,1,1,1,1,1,0,1};
bool rr[2][4]={1,0,1,1,1,1,1,0};
vector < int > ve[2510];
int main() {
    int T;
    scanf("%d", &T);
    while (T--) {
        int n,c,S = 0,k,ms = 0;
        scanf("%d%d%d%d%d%d", &n, &c, &c0, &c1, &d0, &d1);
        for (int i = 1; i <= c; i++) {
            he[i] = 0;
            ve[i].clear();
            bk[i] = false;
        }
        for (int i = 1; i <= n; i++) {
            scanf("%d%d", &wz[i], &sz[i]);
            he[wz[i]] += sz[i];
            ve[wz[i]].push_back(i);
            ty[i] = -1;
            jd[i] = false;
            S += sz[i];
        }
        scanf("%d", &k);
        for (int i = 0; i < k; i++) {
            int a,b;
            scanf("%d%d", &a, &b);
            ty[a] = b;
            bk[wz[a]] = true;
        }
        if (S - c1 > c0 || S - d1 > d0) {
            printf("0\n");
            continue;
        }
        for (int i = 0; i <= n; i++) {
            for (int j = 0; j <= d0; j++) {
                if (i == 0) {
                    f[i][j] = (j == 0);
                    continue;
                }
                f[i][j] = f[i - 1][j];
                if (j >= sz[i] && !bk[wz[i]]) f[i][j] = (f[i][j] + f[i - 1][j - sz[i]]) % md;
            }
        }
        for (int i = 0; i <= d0; i++) tb[i] = f[n][i];
        for (int i = 0; i <= c; i++) {
            for (int j = 0; j <= c0; j++) {
                if (i == 0) {
                    f[i][j] = (j == 0);
                    continue;
                }
                f[i][j] = f[i - 1][j];
                if (j >= he[i] && he[i] != 0 && !bk[i]) f[i][j] = (f[i][j] + f[i - 1][j - he[i]]) % md;
            }
        }
        for (int i = 0; i <= c0; i++) ta[i] = f[c][i];
        for (int i = 0; i <= c0; i++) {
            if (i == 0) sua[i] = ta[i];
            else sua[i] = (sua[i - 1] + ta[i]) % md;
        }
        for (int i = 0; i <= d0; i++) {
            if (i == 0) sub[i] = tb[i];
            else sub[i] = (sub[i - 1] + tb[i]) % md;
        }
        int m = 0;
        for (int i = 1; i <= c; i++) {
            if (bk[i]) {
                for (int j = 0; j < ve[i].size(); j++) {
                    if (ty[ve[i][j]] == -1) {
                        ts[++m] = ve[i][j];
                        ms += sz[ve[i][j]];
                    }
                }
            }
        }
        for (int i = 0; i <= m; i++) {
            for (int j = 0; j <= d0; j++) {
                if (i == 0) {
                    f[i][j] = (j == 0);
                    continue;
                }
                f[i][j] = f[i - 1][j];
                if (j >= sz[ts[i]]) f[i][j] = (f[i][j] + f[i - 1][j - sz[ts[i]]]) % md;
            }
        }
        for (int i = 0; i <= d0; i++) tc[i] = f[m][i];
        m = 0;
        for (int i = 1; i <= c; i++) {
            if (bk[i]) {
                int la;
                for (int j = 0; j < ve[i].size(); j++) {
                    if (ty[ve[i][j]] != -1) {
                        ts[++m] = ve[i][j];
                        la = ve[i][j];
                    }
                }
                jd[la] = true;
            }
        }
        for (re int i = 0, ha = 0, hb = ms; i <= k; i++) {
            if (jd[ts[i]]) ha += he[wz[ts[i]]];
            hb += sz[ts[i]];
            for (re int a = min(c0, ha); a >= 0; a--) {
                for (re int b = min(d0, hb); b >= 0; b--) {
                    for (re int c = 0; c < 2; c++) {
                        if (i == 0) {
                            if (a != 0) g[a][b][c] = 0;
                            else g[a][b][c] = tc[b];
                            continue;
                        }
                        re int la = g[a][b][c];
                        g[a][b][c] = 0;
                        if (jd[ts[i]]) {
                            if (a >= he[wz[ts[i]]]) {
                                if (ya[0][ty[ts[i]]] && b >= sz[ts[i]]) g[a][b][c] += g[a - he[wz[ts[i]]]][b - sz[ts[i]]][0];
                                if (rr[0][ty[ts[i]]]) g[a][b][c] += g[a - he[wz[ts[i]]]][b][0];
                            }
                            g[a][b][c] %= md;
                            if (ya[1][ty[ts[i]]] && b >= sz[ts[i]]) g[a][b][c] = (g[a][b][c] + g[a][b - sz[ts[i]]][1]) % md;
                            if (rr[1][ty[ts[i]]]) g[a][b][c] = (g[a][b][c] + (c == 0 ? g[a][b][1] : la)) % md;
                        } else {
                            if (c == 0) {
                                if (ya[0][ty[ts[i]]] && b >= sz[ts[i]]) g[a][b][c] += g[a][b - sz[ts[i]]][0];
                                if (rr[0][ty[ts[i]]]) g[a][b][c] += la;
                            } else {
                                if (ya[1][ty[ts[i]]] && b >= sz[ts[i]]) g[a][b][c] += g[a][b - sz[ts[i]]][1];
                                if (rr[1][ty[ts[i]]]) g[a][b][c] += la;
                            }
                            g[a][b][c] %= md;
                        }
                    }
                }
            }
        }
        re int ans = 0;
        for (int d = 0; d <= c0; d++) {
            for (int e = 0; e <= d0; e++) {
                if (g[d][e][0]) ans = (ans + 1ll * getsua(S - c1 - d, c0 - d) * getsub(S - d1 - e, d0 - e) % md * g[d][e][0] % md) % md;
            }
        }
        for (int a = 0; a <= c0; a++) {
            for (int b = 0; b <= d0; b++) {
                for (int c = 0; c < 2; c++) g[a][b][c] = 0;
            }
        }
        printf("%d\n", ans);
    }
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值