cdcq

梦幻小鱼干

导航

【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 }
View Code

 

posted on 2019-11-20 19:38  cdcq  阅读(375)  评论(0编辑  收藏  举报