題目
Description
Input
Output
輸出到文件 meal.out 中。
僅一行一個整數,表示所求方案數對 998, 244, 353 取模的結果。
Sample Input
Sample Input1
2 3
1 0 1
0 1 1
Sample Input2
3 3
1 2 3
4 5 0
6 0 0
Sample Input3
5 5
1 0 0 1 1
0 1 0 1 0
1 1 1 1 0
1 0 1 0 1
0 1 1 0 1
Sample Output
Sample Output1
3
Sample Output2
190
Sample Output3
742
Data Constraint
題解
- 作爲聯賽的Day2-T1,理論上應該AC是應該不難的,
- 但是,這道題偏偏難倒了我。。。考場上簡直覺得這是神仙題!!!
- 賽後一想,這題並沒有那麼難,隨着一檔又一檔部分分的實現,我離正解越來越近,
- 不得不說,這是一道質量很高的題(其實聯賽題質量都挺高的),主要體現在分了許多子任務和它們各自的不同與聯繫。
- 開始進入正題——
- 觀察滿分數據大小,猜測正解的複雜度應該是的。
- 先想最簡單的暴力怎麼做?
- 暴力搜索每種烹飪方法選用什麼食材或不選該種烹飪方法,時間複雜度,期望得分32分。
- 發現每次DFS到下一種烹飪方法時,最多一種食材數量改變,
- 又看到有64分數據的特別小,想到可以DP,先枚舉總共選多少種烹飪方法,再設表示當前選到第種食材,種食材分別選了個的方案數,如果或多餘的就恆爲,
- 每次從或或或轉移,須保證不大於。
- 時間複雜度,其中的指數會隨增大而增大,所以時是過不去的,期望得分64分。
- 如果的話,該怎麼記錄呢?開若干維狀態?還是狀態壓縮?還是什麼奇技淫巧?似乎都是不行的。。。
- 要是順着這種思路下去,這題就只能拿到這麼多分了!
- (這就是考場上的我,心涼~~~)
- 重新回到題目,要求每種食材使用次數不能超過總數的一半,想到容斥,用總方案數減去不合法的。
- 總方案數用遞推很好實現,那麼不合法的呢?
- 會發現無論怎麼選,也最多隻會有一種食材出現不合法(這個重要結論似乎很顯然,可沒想到是就是沒想到),,,
- 因此,我們枚舉哪種食材不合法,然後簡單地DP,
- 記錄當前總共選了多少個,且有多少個是不合法的那種。
- 時間複雜度,期望得分84.
- 顯然要優化掉一個,考慮減少一維狀態,
- 改成記錄不合法減去合法的個數,最後這一維大於的部分就是可以累計的答案,
- 時間複雜度,期望得分100!
代碼
#include<cstdio>
#include<cstring>
using namespace std;
#define ll long long
#define md 998244353
ll a[110][2010],f[110][210],sum[2010];
int main()
{
freopen("meal.in","r",stdin);
freopen("meal.out","w",stdout);
int n,m,i,j,k,l;
scanf("%d%d",&n,&m);
for(i=1;i<=n;i++)
for(j=1;j<=m;j++) scanf("%d",&a[i][j]);
f[0][0]=1;
for(i=1;i<=n;i++)
{
sum[i]=0;
for(j=1;j<=m;j++) sum[i]=(sum[i]+a[i][j])%md;
for(j=0;j<=i;j++)
{
f[i][j]=f[i-1][j];
if(j) f[i][j]=(f[i][j]+f[i-1][j-1]*sum[i])%md;
}
}
ll ans=0;
for(i=1;i<=n;i++) ans=(ans+f[n][i])%md;
for(k=1;k<=m;k++)
{
f[0][n]=1;
for(i=1;i<=n;i++)
{
for(j=-i;j<=i;j++)
{
f[i][j+n]=f[i-1][j+n];
if(j>-i) f[i][j+n]=(f[i][j+n]+f[i-1][j-1+n]*a[i][k])%md;
if(j<i) f[i][j+n]=(f[i][j+n]+f[i-1][j+1+n]*(sum[i]-a[i][k]+md))%md;
}
}
for(i=1;i<=n;i++) ans=(ans-f[n][i+n]+md)%md;
}
printf("%lld",ans);
fclose(stdin);
fclose(stdout);
return 0;
}