• Emiya家今天的饭


    前言

    盲猜这是某动漫里的人物

    复习一手去年CSP的题,结果发现只会82pts,还是太弱了QAQ(虽然我去年只会32pts

    题目

    洛谷

    讲解

    part1 32pts

    一个dfs解决,不细讲

    part2 64pts

    考虑(dp[i][s1][s2][s3])表示前i种烹饪方法,(m(m<=3))种主要食材个选多少个的搭配方案

    状态转移方程为:

    [dp[i][s1][s2][s3] = dp[i-1][s1][s2][s3] + dp[i-1][s1-1][s2][s3] * a[i][1] + dp[i-1][s1][s2-1][s3] * a[i][2] + dp[i-1][s1][s2][s3-1] * a[i][3] ]

    时间复杂度(O(n^4))

    part3 82pts

    考虑容斥

    先求出总方案数,同样可以使用dp,令(dp[i][j])表示前(i)个烹饪方法选(j)个的搭配方案数

    [dp[i][j]=dp[i-1][j]+dp[i-1][j-1]*sum_{k=1}^{m}a[i][k] ]

    当然我们可以预处理(s[i]=sum_{k=1}^{m}a[i][k]),总的状态转移方程为:

    [dp[i][j]=dp[i-1][j]+dp[i-1][j-1]*s[i] ]

    最终总方案数为:(sum_{j=1}^{n}dp[n][j])

    然后求出不合法的方案数

    我们发现,如果一个方案不合法,只会是这种情况:有且仅有一个主要食材超过限制

    于是枚举超过限制的主要食材(p),令(f[i][j][k])为前(i)种烹饪方式选(j)种,选到(k)个主要食材(p)的方案数

    [f[i][j][k] = f[i-1][j][k] + f[i-1][j-1][k] * (s[i] - a[i][p]) + f[i-1][j-1][k-1] * a[i][p] ]

    注意做减法的时候有可能减出负数,所以要加一个(MOD),即:

    [f[i][j][k] = f[i-1][j][k] + f[i-1][j-1][k] * (s[i] - a[i][p] + MOD) + f[i-1][j-1][k-1] * a[i][p] ]

    时间复杂度(O(n^3m))

    part4 100pts

    我们根据数据范围猜测正解时间复杂度为(O(n^2m))

    即我们要优化掉part3中的一维才行

    由于我们统计答案的时候只需要考虑(j/2<k)的情况,即我们枚举的超过限制的主要食材(p)的个数要多于其它所有食材(这个转换很关键)

    将它转换为差的形式,(f[i][j])表示前(i)个烹饪方式,(p)与其它食材的差为(j)的方案数

    [f[i][j] = f[i-1][j] + f[i-1][j+1] * (s[i] - a[i][p] + MOD) + f[i-1][j-1] * a[i][p] ]

    统计答案的时候只需要统计(j>0)的情况就好了

    什么,会减到负数,要RE?把第二维所有数字都加上(n)就好了!

    时间复杂度(O(n^2m))

    By the way

    dp的第一维明显可以滚动,但是由于出题人没有卡 我懒得写,所以...

    代码

    int dp[MAXN][MAXN],a[MAXN][MAXM],ans,s[MAXN],f[MAXN][MAXN << 1];
    
    void AM(int &x,LL y)
    {
    	y %= MOD;
    	x += y;
    	if(x >= MOD) x -= MOD;
    }
    
    int main()
    {
    //	freopen("meal.in","r",stdin);
    //	freopen("meal.out","w",stdout);
    	n = Read(); m = Read();
    	for(int i = 1;i <= n;++ i)
    		for(int j = 1;j <= m;++ j)
    			a[i][j] = Read(),AM(s[i],a[i][j]);
    	dp[0][0] = 1;
    	for(int i = 1;i <= n;++ i)
    		for(int j = 0;j <= i;++ j)
    		{
    			dp[i][j] = dp[i-1][j];
    			if(j) AM(dp[i][j],1ll * dp[i-1][j-1] * s[i]);
    			if(i == n) AM(ans,dp[i][j]);
    		}
    	ans--;
    	for(int p = 1;p <= m;++ p)
    	{
    		for(int i = 1;i <= n;++ i)
    			for(int j = 0;j <= 2*n;++ j)
    				f[i][j] = 0;
    		f[0][n] = 1;
    		for(int i = 1;i <= n;++ i)
    			for(int j = n-i;j <= n+i;++ j)
    			{
    				f[i][j] = f[i-1][j];
    				AM(f[i][j],1ll * f[i-1][j+1] * (s[i] - a[i][p] + MOD));
    				if(j-1) AM(f[i][j],1ll * f[i-1][j-1] * a[i][p]);
    				if(i == n && j > n) ans = (ans - f[i][j]) % MOD;
    			}
    	}
    	Put((ans + MOD) % MOD);
    	return 0;
    }
    
  • 相关阅读:
    《Unity3d-控制枪口的朝向代码》
    《Unity3D-鱼的方向 鱼的游动 控制代码》
    《unity3d-随机设置一个物体的scale的方法》
    《Unity3d-在地图中随机生成一个物体的代码》
    《Unity3D-控制检测碰撞以后触发的事件之敌人的攻击行为》
    《Unity3D-播放被打中的时候粒子的特效的代码》
    《Unity3D-敌人血条或者玩家血条控制的代码》
    《Unity3D-自动寻路功能的实现》
    Unity 行首不出现中文标点
    Unity 多级下拉菜单
  • 原文地址:https://www.cnblogs.com/PPLPPL/p/13554014.html
Copyright © 2020-2023  润新知