考场搞了半天还是只能敲暴力。。。
题目链接
题目太繁琐。。。简单点说就是找满足条件的组合数。
深搜暴力。。。(因为我正解的转移推不出来)
首先,第一维表示目前枚举到了第几种做法,第二维表示选了几种做法,第三维表示能组成的组合数。
到当前层,你可以枚举每个不为0的来统计,如果发现可以满足条件,累乘上去,搜索下一层。最后再不要这层枚举,再有一个简单的剪枝,如果后面没有足够的做法数,直接return。最后一位枚举完后累加到答案上即可。
主要代码:
void dfs(int cnt,int tmp,int sum){
if(tmp==ans){
tot=padd(tot,sum,MOD);
return ;
}
if(n-cnt+1+tmp<ans) return ;
for(int i=0;++i<=m;)
if(a[cnt][i]>0){
++num[i];
if(num[i]<=ans/2) dfs(cnt+1,tmp+1,pmul(sum,a[cnt][i],MOD));
--num[i];
}
dfs(cnt+1,tmp,sum);
}
for(;++ans<=n;dfs(1,0,1));
考虑容斥。
考虑枚举不合法的一列。假设我们已经枚举了不合法的列为 c o l col col,接下来会发现我们只关心一个数的位置是否在当前列;如果属于在其他列的情况,那么它具体在哪一列对当前列的合法性并无影响,我们并不需要考虑。
接下来设计状态。 f i , j , k f_{i,j,k} fi,j,k 表示对于 c o l col col 这一列,前 i i i 行在 c o l col col 列中选了 j j j 个,在其他列中选了 k k k 个,那么令 s i s_i si 为第 i i i 行的总和。
那么对于 c o l col col 行则有 f i , j , k = f i − 1 , j , k + a i , c o l ∗ f i − 1 , j − 1 , k + ( s i − a i , c o l ) ∗ f i − 1 , j , k − 1 f_{i,j,k} = f_{i-1,j,k}\ +\ a_{i,col}* f_{i-1,j-1,k}\ +\ (s_i-a_{i,col})* f_{i-1,j,k-1} fi,j,k=fi−1,j,k + ai,col∗fi−1,j−1,k + (si−ai,col)∗fi−1,j,k−1
再统计 ∑ j > k f n , j , k \sum_{j>k} f_{n,j,k} ∑j>kfn,j,k 的和并对每一列求和,就求出了所有不合法的方案数。
再考虑方案总数。设 g i , j g_{i,j} gi,j 为前 i i i 行共选了 j j j 个的方案数。
那么就有转移 g i , j = g i − 1 , j + s i ∗ g i − 1 , j − 1 g_{i,j}=g_{i-1,j}+s_i*g_{i-1,j-1} gi,j=gi−1,j+si∗gi−1,j−1。
然后你就得到了84分的高分。
具体代码和正解并一起了。
用剪枝减去无用状态。
在不合法情况的计算过程中,也就是 f i , j , k f_{i,j,k} fi,j,k 的转移过程中,我们并不关心 j , k j,k j,k 的具体数值,而只关心相对的大小关系,所以我们可以将状态变为 f i , j f_{i,j} fi,j,表示前 i i i 行,当前列的数比其他列的数多了 j j j 个。
那么就可以这么转移了: f i , j = f i − 1 , j + a i , c o l ∗ f i − 1 , j − 1 + ( s i − a i , c o l ) ∗ f i − 1 , j + 1 f_{i,j} = f_{i-1,j}\ +\ a_{i,col}* f_{i-1,j-1}\ +\ (s_i-a_{i,col})* f_{i-1,j+1} fi,j=fi−1,j + ai,col∗fi−1,j−1 + (si−ai,col)∗fi−1,j+1
(代码中可能有些变量不太一样,这只是为了上面好看点所以emmm。。。)
满分code:
#include
using namespace std;
const int N=100+5,M=2e3+10,MOD=998244353;
int n,m,a[N][M],b[N][M];
long long f[N][M<<1],g[N][M],sum=0;
void freo(){
freopen("meal.in","r",stdin);
freopen("meal.out","w",stdout);
}
int padd(int a,int b){
return a+b>=MOD?a+b-MOD:a+b;
}
int psub(int a,int b){
return a-b<0?a-b+MOD:a-b;
}
int pmul(int a,int b){
return (long long)a*b%MOD;
}
void init(){
scanf("%d%d",&n,&m);
for(int i=0;++i<=n;)
for(int j=0;++j<=m;scanf("%d",&a[i][j]),b[i][0]=padd(b[i][0],a[i][j]));
}
void work(){
for(int i=0;++i<=n;) for(int j=0;++j<=m;b[i][j]=psub(b[i][0],a[i][j]));
for(int i=0;++i<=m;){
memset(f,0,sizeof(f));
f[0][n]=1;
for(int j=0;++j<=n;)
for(int h=n-j-1;++h<=n+j;
f[j][h]=padd(padd(f[j-1][h],pmul(f[j-1][h-1],a[j][i])),
pmul(f[j-1][h+1],b[j][i])));
for(int j=0;++j<=n;sum=padd(sum,f[n][n+j]));
}
g[0][0]=1;
for(int i=0;++i<=n;)
for(int j=-1;++j<=n;g[i][j]=padd(g[i-1][j],pmul(g[i-1][j-1],b[i][0])));
for(int i=0;++i<=n;sum=psub(sum,g[n][i]));
}
void prin(){
printf("%d",pmul((int)sum,MOD-1));
}
int main(){
// freo();
init();
work();
prin();
return 0;
}