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 longusing 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 ...