首页 > 其他分享 >【题解】[CSP-S2019] Emiya 家今天的饭

【题解】[CSP-S2019] Emiya 家今天的饭

时间:2023-02-02 19:56:02浏览次数:55  
标签:方案 int 题解 times 选择 Emiya S2019 dp MOD

题目分析:

(我竟然可以独立做出来正赛的题,表示震惊)
这个题面显然就很神仙,不好分析,我们进行转化一下题意:
给定一个 \(n \times m\) 的矩阵,对于每一行我们可以选择一个数也可以不选择,要求至少选择一个数,而且对于任意一列其被选择的次数都必须不超过总选择的点数的一半,一次选择的方案数就是所有选择的数的乘积,求所有的选择方案的方案数。

对于 \(m \le 3\),直接记录每一列选择了多少个然后去转移就好了,复杂度 \(O(n^{m+1})\),\(40\) 分到手

看到这个小于等于的限制,应该会象征性地向容斥想想,发现容斥出来也不好做的样子(?)
我们发现容斥的话其实就是无限制的方案数减去大于 \(\frac{k}{2}\) 的方案数,而我们大于 \(\frac{k}{2}\) 最多只有一个,也就是说我们容斥之后只有 \(O(n)\) 个状态是有用的,也就是可以去直接枚举哪一列大于 \(\frac{k}{2}\)。
最后用所有列都没有限制的方案,减去这些所有的方案数的和就好了。

如果能想到这个结论应该就可以发现这肯定是一个很重要的结论,因为看上去就非常有用,这样的话大概就可以想到下面这种思路:
枚举列 \(m\) 大于 \(\frac{k}{2}\),设 \(dp[i][j][k]\) 表示前 \(i\) 行,选择了 \(j\) 个,第 \(m\) 列选择了 \(k\) 个的方案数,转移即:

\[dp[i][j][k] = dp[i-1][j][k] + s[i][m] \times dp[i-1][j-1][k] + a[i][m] \times dp[i-1][j-1][k-1] \]

我们上面设 \(s[i][j]\) 表示第 \(i\) 行除去第 \(j\) 个数的和。

这样的话就可以做到 \(O(n^3m)\),就可以得到 \(84\) 分可以跑路了

发现这个方程其实推到这里就没什么前途了,必须另外想想别的办法,因为其实这种状态最后统计答案也很麻烦。稍微想想就能发现,其实所谓的超过一半,就意味着比其他的列选择的多啊,类似绝对众数那样,所以只需要维护第 \(m\) 列被选择的次数与其他列被选择的次数的差就好了。
也就是设 \(f[i][j]\) 表示前 \(i\) 行,第 \(m\) 列被选择的次数减去其他列被选择的次数为 \(j\) 的方案数,转移:

\[f[i][j] = f[i-1][j] + s[i][m] \times f[i-1][j+1] + a[i][m] \times f[i-1][j-1] \]

最后答案的统计就是所有 \(\sum_{j = 1}^{n} dp[n][j]\)
这样的复杂度就是 \(O(n^2m)\)

还剩一个问题就是所有列都无限制的方案数,这个方案数显然就是:\(-1 + \prod_{i=1}^n (s[i] + 1)\),因为每行都可以不选,但是不能都不选。

代码:

点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MOD = 998244353,N = 105,M = 2005;
int sum[N][M],a[N][M];
int f[N][2*N];
int mod(int x){
	return (x%MOD + MOD)%MOD; 
}
signed main(){
	int n,m;scanf("%lld%lld",&n,&m);
	for(int i=1; i<=n; i++){
		for(int j=1; j<=m; j++){
			scanf("%lld",&a[i][j]);
			sum[i][0] = mod(sum[i][0] + a[i][j]);
		}
	}
	for(int i=1; i<=n; i++){
		for(int j=1; j<=m; j++){
			sum[i][j] = mod(sum[i][0] - a[i][j]);
		}
	}
	int ans = 1;
	for(int i=1; i<=n; i++)	ans = mod(ans * (sum[i][0] + 1));
	ans = mod(ans - 1);
	for(int tmp = 1;tmp <= m; tmp++){
		memset(f,0,sizeof(f));
		f[0][n] = 1;
		for(int i=1; i<=n; i++){
			for(int j=n-i; j<=n+i; j++){
				f[i][j] = mod(f[i-1][j] + f[i-1][j+1] * sum[i][tmp] + f[i-1][j-1] * a[i][tmp]);
			}
		}
		for(int i=1; i<=n; i++)	ans = mod(ans - f[n][n+i]);
	}
	printf("%lld\n",ans); 
	return 0;
}

标签:方案,int,题解,times,选择,Emiya,S2019,dp,MOD
From: https://www.cnblogs.com/linyihdfj/p/17087193.html

相关文章

  • CF1311E 题解
    题意:构造一个节点数为\(n\)的树,使得节点深度之和为\(d\).(根节点为\(1\)且深度为\(0\))显然,不断构造二叉树并检查是否为答案是行不通的,只能在\(O(n)\)的......
  • [题解] P2685 [TJOI2012]桥 思路整理
    题目大意给一张\(n\)个点\(m\)条边的图,求删去一条边后最短路长度的最大值与对应删边方案数。思路首先考虑,如果删去的这条边不在原图最短路上,那么新图最短路长度与原......
  • 【题解】[FJOI2017]矩阵填数
    题目分析:最大值为\(v\)的限制显然可以转化为\(\lev\)的方案数减去\(\lev-1\)的方案数。因为这里有很多个这种限制所以直接容斥就好了,具体来说就是枚举哪些条件取......
  • 【题解】Luogu DTOI Round 4
    前言:好久不打洛谷的\(rated\)赛了,结果一打\(205,rk14\),看来是没有大佬来打。放一下链接:https://www.luogu.com.cn/contest/96429P8976「DTOI-4」排列题目分析:这个......
  • CF1037H Security题解
    根据字典序的定义,位置大的大于长度长的,长度长的大于长度短的。所以我们贪心,先追求长度长的,再追求后面的位置大的,再追求前面的位置大的。我们要一个能遍历子串的结构,就选......
  • 关于“等保保护”最常见问题解答!
    等保全称信息安全等级保护,它是网络安全体系中非常重要的组成部分。但很多人对等保不够了解,所以小编特整理了这篇文章,希望对你们有帮助。1、等级保护是强制性的吗?可......
  • 【题解】CF1770F Koxia and Sequence
    有没有觉得其他题解的模二Lucas逆用太智慧了,有没有觉得这题的第一思路是直接拆位算每一位是否有贡献,而不是先满足和的限制列式?这里提供另外一个做法。方向不同,结果一样......
  • Codeforces Round #848 (Div. 2) A~F 题解
    A.FlipFlopSum能换\(-1,-1\)就换,不能能换\(1,-1\)或\(-1,1\)也可以,否则只能换\(1,1\)。B.TheForbiddenPermutation如果原序列一开始就是好的那就结束,否则......
  • 关于github上README.md图片显示不出来的问题解决办法
    关于github上README.md图片显示不出来的问题解决办法出现原因:如果你的README文件内显示图片的路径是正确的,那么很有可能是因为DNS被污染了所以导致显示不正常,即无法访问存放......
  • 【PER #1】捉迷藏 / Ptz2022 Day1.Kyoto U L 题解
    今天心血来潮想改一改pj的题,发现了这场easyround的A还没改……跟自己和解了,想了两天没想明白,说说大致思路。题目链接只考虑一组询问怎么做,先把\(v\)当作根,称......