【NOIP/CSP2019】D2T1 Emiya 家今天的饭
这个D2T1有点难度啊
原题:
花了我一下午的时间,作为D2T1的确反常
条件很奇怪,感觉不太直观,于是看数据范围先写了个暴力
写暴力的时候我就注意到了之前没有仔细想过的点,烹饪方式必须不同
虽然a很大,但是n只有100,即总菜数比较小
而且虽然m和a都很大,但是一种方法只能做一道菜,即选一种食材
所以搜索枚举每种方法选哪种食材,最后检查方案是否满足条件
这样可以拿到32分
需要注意到一点很关键的性质
不管什么方案,最多只有一个食材能超过n/2,看到n/2要敏感
那么如果无视n/2的限制,直接dp就vans了,接下来只需要去掉不合法的方案
枚举哪一种食材超过n/2,可以发现此时食材只有两种,枚举到的食材和其他的食材
需要表达的状态数大大减少
设计状态,f[i][j][k]表示直到第i种方式,总共选了j道菜,其中k道菜用当前枚举食材
这样可以拿到82分
然后就不会了233
这个递推又没有决策,咋优化啊
本着单个题目思考时间不能过长的原则,观摩了python96的题解:
https://blog.csdn.net/weixin_37517391/article/details/103110646
立刻点化233
确实不太好想,果然适时看题解效率更高
我们记录状态的时候不必记录两类食材的数量,只需记录二者差值即可
只要差值相同,不同的数量对于合法性的判断都是等价的
那么以前的3维dp就优化到2维,可以ac了
代码:
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 #include<cmath> 6 using namespace std; 7 #define LL long long 8 const int mo=998244353; 9 int rd(){int z=0,mk=1; char ch=getchar(); 10 while(ch<'0'||ch>'9'){if(ch=='-')mk=-1; ch=getchar();} 11 while(ch>='0'&&ch<='9'){z=(z<<3)+(z<<1)+ch-'0'; ch=getchar();} 12 return z*mk; 13 } 14 int n,m,a[110][2100]; 15 LL ans=0; 16 int cnt[2100]; 17 LL s[110]; 18 LL f7[110][110][110]; 19 LL f[110][210]; 20 bool chck(int x){ 21 if(!x) return false; 22 //注意x=0时必须特判,因为此时cnt也全是0,下面条件不会成立 23 for(int i=1;i<=m;++i)if(cnt[i]>x/2) return false; 24 return true; 25 } 26 void dfs(int x,int y,LL z){ 27 if(x==n+1){ 28 if(chck(y)) ans=(ans+z)%mo; 29 return ; 30 } 31 dfs(x+1,y,z); 32 for(int i=1;i<=m;++i)if(a[x][i]){ 33 ++cnt[i]; 34 dfs(x+1,y+1,z*a[x][i]%mo); 35 --cnt[i]; 36 } 37 } 38 LL gan(int x){ 39 memset(f7,0,sizeof(f7)); 40 f7[0][0][0]=1; 41 for(int i=1;i<=n;++i)for(int j=0;j<=i;++j)for(int k=0;k<=j;++k){ 42 f7[i][j][k]=f7[i-1][j][k]; 43 if(j && k) f7[i][j][k]=(f7[i][j][k]+f7[i-1][j-1][k-1]*a[i][x]%mo)%mo; 44 if(j) f7[i][j][k]=(f7[i][j][k]+f7[i-1][j-1][k]*(s[i]-a[i][x])%mo)%mo; 45 } 46 LL bwl=0; 47 for(int i=1;i<=n;++i)for(int j=i/2+1;j<=i;++j) bwl=(bwl+f7[n][i][j])%mo; 48 return bwl; 49 } 50 LL ganisok(int x){ 51 memset(f,0,sizeof(f)); 52 f[0][n]=1; 53 for(int i=1;i<=n;++i)for(int j=0;j<=n+n;++j){ 54 f[i][j]=f[i-1][j]; 55 if(j) f[i][j]=(f[i][j]+f[i-1][j-1]*a[i][x]%mo)%mo; 56 if(j<n+n) f[i][j]=(f[i][j]+f[i-1][j+1]*(s[i]-a[i][x]%mo))%mo; 57 } 58 LL bwl=0; 59 for(int i=n+1;i<=n+n;++i) bwl=(bwl+f[n][i])%mo; 60 return bwl; 61 } 62 int main(){ 63 //freopen("ddd.in","r",stdin); 64 cin>>n>>m; 65 for(int i=1;i<=n;++i)for(int j=1;j<=m;++j) a[i][j]=rd(); 66 if(n<=10 && m<=3){ 67 dfs(1,0,1); 68 cout<<ans<<endl; 69 } 70 else if(n<=40 && m<=500){ 71 for(int i=1;i<=n;++i){ 72 s[i]=0; 73 for(int j=1;j<=m;++j) s[i]=(s[i]+a[i][j])%mo; 74 } 75 LL bwl=1; 76 for(int i=1;i<=n;++i) bwl=bwl*(s[i]+1)%mo; 77 bwl-=1; 78 for(int i=1;i<=m;++i) bwl=(bwl-gan(i))%mo; 79 cout<<(bwl%mo+mo)%mo<<endl; 80 } 81 else{ 82 for(int i=1;i<=n;++i){ 83 s[i]=0; 84 for(int j=1;j<=m;++j) s[i]=(s[i]+a[i][j])%mo; 85 } 86 LL bwl=1; 87 for(int i=1;i<=n;++i) bwl=bwl*(s[i]+1)%mo; 88 bwl-=1; 89 for(int i=1;i<=m;++i) bwl=(bwl-ganisok(i))%mo; 90 cout<<(bwl%mo+mo)%mo<<endl; 91 } 92 return 0; 93 }