2019 CSP-S Day2-T1 Emiya 家今天的飯

題目

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是應該不難的,
  • 但是,這道題偏偏難倒了我。。。考場上簡直覺得這是神仙題!!!
  • 賽後一想,這題並沒有那麼難,隨着一檔又一檔部分分的實現,我離正解越來越近,
  • 不得不說,這是一道質量很高的題(其實聯賽題質量都挺高的),主要體現在分了許多子任務和它們各自的不同與聯繫。
  • 開始進入正題——
  • 觀察滿分數據大小,猜測正解的複雜度應該是O(n2m)O(n^2m)的。
  • 先想最簡單的暴力怎麼做?
  • 暴力搜索每種烹飪方法選用什麼食材或不選該種烹飪方法,時間複雜度O(mn)O(m^n),期望得分32分。
  • 發現每次DFS到下一種烹飪方法時,最多一種食材數量改變,
  • 又看到有64分數據的mm特別小,想到可以DP,先枚舉總共選多少種烹飪方法,再設f[i][j][k][l]f[i][j][k][l]表示當前選到第ii種食材,mm種食材分別選了j,k,lj,k,l個的方案數,如果m=1m=122多餘的k,lk,l就恆爲00
  • 每次從f[i1][j][k][l]f[i-1][j][k][l]f[i1][j1][k][l]f[i-1][j-1][k][l]f[i1][j][k1][l]f[i-1][j][k-1][l]f[i1][j][k][l1]f[i-1][j][k][l-1]轉移,須保證j,k,lj,k,l不大於n2\frac{n}{2}
  • 時間複雜度O(n5)O(n^5),其中nn的指數會隨mm增大而增大,所以m>3m>3時是過不去的,期望得分64分。
  • 如果m>3m>3的話,該怎麼記錄呢?開若干維狀態?還是狀態壓縮?還是什麼奇技淫巧?似乎都是不行的。。。
  • 要是順着這種思路下去,這題就只能拿到這麼多分了!
  • (這就是考場上的我,心涼~~~)
  • 重新回到題目,要求每種食材使用次數不能超過總數的一半,想到容斥,用總方案數減去不合法的。
  • 總方案數用遞推很好實現,那麼不合法的呢?
  • 會發現無論怎麼選,也最多隻會有一種食材出現不合法(這個重要結論似乎很顯然,可沒想到是就是沒想到),,,
  • 因此,我們枚舉哪種食材不合法,然後簡單地DP,
  • 記錄當前總共選了多少個,且有多少個是不合法的那種。
  • 時間複雜度O(n3m)O(n^3m),期望得分84.
  • 顯然要優化掉一個nn,考慮減少一維狀態,
  • 改成記錄不合法減去合法的個數,最後這一維大於00的部分就是可以累計的答案,
  • 時間複雜度O(n2m)O(n^2m),期望得分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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章