題面傳送門
簡述題意:
給定乙個矩陣,n行m列,每一行最多選1個位置打上標記,且不存在某一列所有有標記的點的數量大於全部的一半,至少給乙個點打上標記,求方案數。答案對998244353取模。
很水的暴力dp可以得84分,但100分的優化做法值得很多題借鑑。
先說簡單說一下暴力:
我們直接求滿足上面條件的方案數並不容易,所以進行問題轉化,轉化為:(每行選不超過乙個的方案數-每行選不超過乙個且某一列選了超過一半的方案數)。
至於每行選不超過乙個的方案數很容易得到:設$g[i][j]$表示前i行共選j的數的且每行選不超過乙個的方案數,得到轉移方程:$g[i][j]=g[i-1][j]+s_i*g[i-1][j-1]$。其中$s_i$表示第i行的數值和。
對於每行選不超過乙個且某一列選了超過一半的方案數:列舉超過總數一半的某一列x:設$f[i][j][k]$表示前i行中第x列選了j個,非第x列選了k個的符合要求的方案數。
容易得到轉移方程:$f[i][j][k]=f[i-1][j][k]+a[i][x]*f[i-1][j-1][k]+(sum[i]-a[i][x])*f[i-1][j][k-1]$
輕鬆分析出:時間複雜度是$o(mn^3)$
然後便是很有用的優化:
我們注意到,這個dp方程似乎不能使用什麼資料結構進行優化,而且也無法進行省略某狀態等操作。但是這個方程中第二維和第三維在我們求方案數的時候並不是一定要知道數值是什麼,只要知道j和k的大小關係即可。那麼我們可以壓縮狀態:$f[i][j]$表示前i行,當前列的數比其他列的數多了j個的符合要求的方案數。也就說壓縮後的第二維等與壓縮前的(第二維-第一維)。
這樣可以以$o(mn^2)$的複雜度得到期望100分的好成績。
#include #define inc(i,a,b) for(register int i=a;i<=b;i++)using
namespace
std;
intn,m;
const
int p=998244353
;long
long sum[110][2100],f[110][220
];long
long ans1,ans2,a[110][2100
];int
main()
inc(l,
1,m)
inc(j,
1,n) ans2=(ans2+f[n][n+j])%p;
}memset(f,
0,sizeof(f)); f[0][0]=1
; inc(i,
1,n) inc(j,0,n) f[i][j]=(f[i-1][j]+((j>=1)?f[i-1][j-1]*sum[i][0]%p:0))%p;
inc(i,
1,n) ans1=(ans1+f[n][i])%p;
long
long ans=((ans1-ans2)%p+p)%p;
cout
}
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 ...
Luogu P5664 Emiya 家今天的飯
重做下去年的csp題找找感覺,去年d1t2寫可持久化線段樹上二分的悲慘經歷讓我對d1t2充滿了厭惡 好吧其實是正解在簡單都懶得寫了 因此就來改這個去年沒調出來的dp了 首先這個主要食材佔一半一眼容斥,因此我們大體思路就有了 先求出不管這個限制的總方案數,設 f 表示前 i 種方法中做了 j 道菜的方...
P5664 Emiya 家今天的飯
miku 這個題很顯然的可以從部分分推到正解 64上去就是乙個四維dp,dp i j k z 表示在第1行的時候第一行選了j個,第2行選了k個,第3行選了z個的 情況下的方案數,轉移用手就能推。只是有個小細節 include include include include define int lo...