CSP S2019 Emiya 家今天的飯 題解

2022-05-27 15:09:12 字數 1499 閱讀 7723

csp-s2 2019 d2t1

很不錯的一題dp,通過這道題學到了很多。

身為乙個對dp一竅不通的蒟蒻,在考場上還掙扎了1h來推式子,居然還有幾次幾乎推出正解,然而最後還是只能打個32分的暴搜滾粗

題意分析

給出乙個矩陣,要求每行只能選乙個節點,每列選的節點不能超過所有選的節點的一半,不能不選,給出每個節點的選擇方案數,求總方案數

思路分析

可以看出,維護每列已選的節點複雜度太大,不太可行;因此很容易想到,先不考慮每列不超過一半的這個限制,求出總方案數,然後再減去考慮這個限制後不合法的方案數。現在問題就變成,求任意列選的節點超過所有選的節點的一半的方案數之和。

顯然,在乙個方案中,只可能有一列的節點超過所有選的節點的一半。因此可以想到列舉這個超過限制的列,然後對於這個列進行dp求解。

具體實現

設$f_$表示前$i$行選$j$個節點,當前列舉到的列選$k$個節點的方案數。對於每個列,複雜度為$o(n^3)$,總的複雜度為$o(mn^3)$,可以得到84分的高分。

想得到滿分還需要進一步優化。考慮將某兩個狀態合併。觀察狀態,實際上我們想知道的只是$j,k$的大小關係,對於具體的值並不關心,考慮將它們合併到一維。

考慮我們需要的限制條件$k>\left \lfloor \frac \right \rfloor$,變形一下可以得到$2k+n-j>n$。觀察這個式子,可以發現,$n-j$就是這$n$行裡沒有選的行數。然後乙個奇妙的想法就出來了,對於每個節點,選它時當做該列選了兩次,而對於某一行不選時,當做所有列選了一次,最終要找的就是當前列被選超過$n$次的方案。這樣就成功地優化掉了第二維。

給一下狀態轉移方程:

f[j][k]=(f[j][k]+f[j-1][k]*(cnt[j]-w[j][i]))%p;//不選當前列

f[j][k+1]=(f[j][k+1]+f[j-1][k])%p;//不選當前行

f[j][k+2]=(f[j][k+2]+f[j-1][k]*w[j][i])%p;//選當前行當前列對應的節點

注意取模時出現負數的情況,記得開long long。

#include#include#include#define ll long long

using namespace std;

const int n=200,m=3000,p=998244353;//fft(霧

int n,m;

ll ans=1;

ll cnt[n],w[n][m],f[n][m];

int main()

ans=(ans+p-1)%p;//減去全部不選的情況

for(int i=1;i<=m;i++)

for(int j=n+1;j<=2*n;j++)

ans=(ans+p-f[n][j])%p;//減去當前列舉到的不合法方案

}printf("%lld",ans);

return 0;

}

CSP S 2019 Emiya 家今天的飯

類似 烏龜棋 的思想,由於 64pts 的 m 3 非常小。我們可以設乙個 dp 建立 m 個維度存下每種物品選了幾次 狀態轉移 根據題意,每種烹飪方法最多選一道菜。答案 sum sum sum f n a b c max a,b,c lfloor a b c 2 rfloor 且 a b c 0 ...

CSP S 2019 Emiya 家今天的飯

loj 3211 看到題目中要求每種主要食材至多在一半的菜中被使用,容易想到補集轉換。即 ans 總方案數 存在某一種食材在一半以上的菜中被使用的方案。總方案數很容易求 即對於每一種烹飪方法選至多一道菜的方案為 s i 1 其中 s i sum a 故總方案數 prod s i 1 1 其中 1是因...

csp2019 Emiya家今天的飯

作為提高組d2t 1d2t1 d2t1 比去年難 所以這道題我打的特別的差 這道題我們很顯然可以看到可以打乙個暴力 複雜度o n n o n n o n n 我考場上就達到了這裡 我太菜了 void dfs int u,ll plus dfs u 1,plus rep i,1 m if a u i ...