題解-十二省聯考2019 皮配

Problem

\(\mathrm{loj3051}\)c++

題意概要:有 \(c\) 個豆莢,共 \(n\) 顆豆子,每顆豆子都有本身的重量,如今須要將給豆子設定爲 (黃色/綠色,圓粒/皺粒),要求知足如下條件:git

  • 給定這四種性狀的閥值 \(C_0,C_1,D_0,D_1\),要求爲這種性狀的豆子重量和不能超過該閥值
  • 與此同時,這 \(n\) 顆豆子中存在 \(k\) 顆頑皮豆,頑皮豆都有本身的想法,好比拒絕成爲 (黃圓/黃皺/綠圓/綠皺)
  • 同一個豆莢裏的豆子必須 同時爲黃色同時爲綠色

求有多少種給豆子設定的方案,對 \(998244353\) 取模數組

\(n,c\leq 10^3,k\leq 30\)spa

\(M=\max\{C_0,C_1,D_0,D_1\}\)\(M\leq 2500\)線程

豆子重量不超過 \(\min\{M,10\}\)code

Solution

首先有一個 \(O(nM^3)\) 的暴力:設定三維——「黃圓」、「黃皺」和「綠圓」的豆子重量和,枚舉每一顆豆子去更新。醬紫就有 \(30pts+\)ci

其次有一個 \(O(nM^2)\) 的暴力:設定兩維——「黃色」和「圓粒」的重量和。醬紫有 \(50pts\)get


再者考慮 \(k=0\):全部豆子都沒有限制,考慮將豆子進行劃分。發現不管是先劃分 黃/綠 仍是先劃分 圓/皺 都對結果沒有影響,對應的這二者能夠分開計算最後相乘:it

\(f[i]\) 表示圓粒重量和爲 \(i\) 的方案數,\(g[i]\) 表示圓粒重量和爲 \(i\) 的方案數,這兩個數組能夠 \(O(nM)\) 揹包求得,而後答案就爲(設全部豆子重量和爲 \(S\)):io

\[\sum_{i=S-C_1}^{C_0}\sum_{j=S-D_1}^{D_0}f[i]g[j]\]

作到這再算上前邊的就有 \(70pts\)


如今考慮將頑皮豆加入 肯德基豪華午飯 考慮範疇

稱這些頑皮豆爲「有毒」的豆子,對應的豆莢必定爲「有毒」的豆莢

(在閱讀下面內容時,請時刻牢記:對於 黃/綠 的劃分是以「豆莢」爲單位的;對於 圓/皺 的劃分是以「豆子」爲單位的)

  • 對於「無毒」的豆莢,仍然能夠正常考慮其 黃/綠 的相對性狀,dp 數組設爲 \(f\)
  • 對於「無毒」的豆子,仍然能夠正常考慮其 圓/皺 的相對性狀,dp 數組設爲 \(g\)(由於「有毒」的豆子可能會影響整個豆莢的 黃/綠 性狀,因此這裏不考慮顏色性狀)

那麼依照前一檔部分分,這兩個是能夠乘起來的

不難發現,求完 \(f\) 後就不用再管「無毒」豆莢的 黃/綠,求完 \(g\) 後就不用再管「無毒」豆子的 圓/皺,餘下須要考慮的就只有「有毒」豆子的 黃/綠、圓/皺 了

因爲「有毒」的豆子很少,只有 \(30\) 個,能夠對這一部分考慮採用 \(50pts\) 那一檔的暴力 Dp,dp 數組設爲 \(F\)(注意 黃/綠 維度用的是整個豆莢的重量,圓/皺 維度用的是單個豆子的重量)

最後只要枚舉「有毒」豆子的兩個性狀的重量 \(F\),會獲得一個 黃/綠 劃分和一個 圓/皺 劃分,考慮用 \(f\) 去匹配 黃/綠(這樣全部「有毒」與「無毒」豆莢的 黃/綠 就處理完了),用 \(g\) 去匹配 圓/皺 劃分(這樣全部「有毒」與「無毒」的豆子的 圓/皺 就處理完了),乘起來便可

計算時間複雜度(空間反正是 \(O(M^2)\) 的):

  • 計算 \(f\) 的 dp 是 \(O(cM)\)
  • 計算 \(g\) 的 dp 是 \(O(nM)\)
  • 計算 \(F\) 的 dp 是 \(O(kM^2)\) 的,可是因爲豆子的重量 \(s\leq 10\),因此實際上是 \(O(k^2sM)\)
  • 統計答案是 \(O(ksM)\)

總時間複雜度爲 \(O((c+n)M+k^2sM)\)

另說,這題只考察了聯賽級別的知識點——揹包,包括:分部揹包、雙線程揹包、揹包合併……

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

inline void read(int&x){
    char ch=getchar();x=0;while(!isdigit(ch))ch=getchar();
    while(isdigit(ch))x=x*10+ch-'0',ch=getchar();
}

const int p = 998244353;
inline int qm(const int x) {return x < p ? x : x - p;}
inline void pls(int&x, const int&y) {x = x+y < p ? x+y : x+y-p;}

const int N = 1010, M = 2503;
bool city_hate[N];
int city_sum[N], hate[N], b[N], s[N];
int f[M], pre_f[M], F[M][M];
int g[M], pre_g[M], G[M][M];
int n, c;

void work() {
    read(n), read(c);
    int C0, C1, D0, D1, ALL = 0;
    for(int i=1;i<=c;++i) city_hate[i] = false, city_sum[i] = 0;
    read(C0), read(C1), read(D0), read(D1);
    for(int i=1;i<=n;++i) read(b[i]), read(s[i]), city_sum[b[i]] += s[i], hate[i] = -1, ALL += s[i];
    int K, x; read(K);
    while(K--) read(x), read(hate[x]), city_hate[b[x]] = true;
    
    memset(f, 0, sizeof f), pre_f[0] = f[0] = 1;
    for(int i=1;i<=c;++i)
        if(!city_hate[i] and city_sum[i])
            for(int j = C0, ts = city_sum[i]; j >= ts; -- j)
                pls(f[j], f[j-ts]);
    for(int i=1;i<=C0;++i) pre_f[i] = qm(pre_f[i-1] + f[i]);
    
    memset(g, 0, sizeof g), pre_g[0] = g[0] = 1;
    for(int i=1;i<=n;++i)
        if(-1 == hate[i])
            for(int j = D0, ts = s[i]; j >= ts; -- j)
                pls(g[j], g[j-ts]);
    for(int i=1;i<=D0;++i) pre_g[i] = qm(pre_g[i-1] + g[i]);
    
    int Cs = 0, Ss = 0;
    memset(F, 0, sizeof F), F[0][0] = 1;
    memset(G, 0, sizeof G);
    for(int ct = 1; ct <= c; ++ ct)
        if(city_hate[ct]) {
            Cs += city_sum[ct], Cs = min(Cs, C0);
            for(int i=0;i<=Cs;++i)
            for(int j=0;j<=Ss;++j) G[i][j] = F[i][j];
            
            for(int a=1;a<=n;++a)
                if(b[a] == ct and ~hate[a]) {
                    const int t = s[a];
                    Ss += t, Ss = min(Ss, D0);
                    if(hate[a] == 1)
                        for(int i=0;i<=Cs;++i) {
                            for(int j=Ss;j>=t;--j) F[i][j] = F[i][j-t];
                            for(int j=t-1;~j;--j) F[i][j] = 0;
                        }
                    if(hate[a] >= 2)
                        for(int i=0;i<=Cs;++i)
                        for(int j=Ss;j>=t;--j) pls(F[i][j], F[i][j-t]);
                    if(hate[a] == 3)
                        for(int i=0;i<=Cs;++i) {
                            for(int j=Ss;j>=t;--j) G[i][j] = G[i][j-t];
                            for(int j=t-1;~j;--j) G[i][j] = 0;
                        }
                    if(hate[a] <= 1)
                        for(int i=0;i<=Cs;++i)
                        for(int j=Ss;j>=t;--j) pls(G[i][j], G[i][j-t]);
                }
            for(int j=0,t=city_sum[ct];j<=Ss;++j) {
                for(int i=Cs;i>=t;--i) F[i][j] = F[i-t][j];
                for(int i=t-1;~i;--i) F[i][j] = 0;
            }
            for(int i=0;i<=Cs;++i)
            for(int j=0;j<=Ss;++j)
                pls(F[i][j], G[i][j]);
        }
    
    int res = 0;
    for(int i=0;i<=Cs;++i)
    for(int j=0;j<=Ss;++j) {
        int l1 = max(0, ALL - C1 - i), r1 = C0 - i; if(l1 > r1) continue;
        int l2 = max(0, ALL - D1 - j), r2 = D0 - j; if(l2 > r2) continue;
        int vf = pre_f[r1]; if(l1) vf += p - pre_f[l1-1];
        int vg = pre_g[r2]; if(l2) vg += p - pre_g[l2-1];
        pls(res, (ll)vf * vg%p * F[i][j]%p);
    }
    printf("%d\n", res);
}

int main() {
    int T; read(T);
    while(T--) work();
    return 0;
}
相關文章
相關標籤/搜索