zoukankan      html  css  js  c++  java
  • 【洛谷P2150】[NOI2015] 寿司晚宴

    前言

    【题目传送门】
    本题之前在 (lyn) 大佬讲课的时候讲过,但当时没怎么听懂,只记得是分解质因数然后状压。

    题解

    设计 DP

    从状压入手。
    首先考虑朴素 DP。
    一开始我想到设计一维 (dp_{stat}) 表示一个人拿的数字的质因子集合,从此可以推出另一个人可以选择的物品。但是这样转移的时候不知道哪些物品是否被选,实际上也就不能推出另一个人可以选择的物品
    这样不行那就再加一维,记 (dp(i,j)) 表示小 G,小 W 选的质因子集合分别为 (i,j)。本来还应该有一维记录当前选到第几个数字,但是通过倒序枚举就可以滚动数组优化掉。

    优化 DP

    上面的方法适用于质因子数量较少的情况,而当 (n=500) 时有 (100) 个左右的质数。
    考虑状压本质,其实就是为了防止两个集合有交集。发现 (500) 以下的数字最大的质因子如果不小于 (22),最多只有一个。所以单独记录一维表示是否有大质因子。
    具体实现上,把大质因子相同的数字排列成一段,用两个定义和 (dp) 数组相同的辅助数组 (f1,f2) 分别转移两个人选择包含当前这个大质因子的数(同一段的数字要选只能一个人选),这样也不用给原本的 DP 加维了,每段结束的时候把 (f1,f2) 的值传递给 (dp) 数组即可。

    一点问题

    关于一些边界的判断和细节也要格外小心,即使是看了题解也没有一次过。

    • 转移 DP 出现减法的时候没有加模数。
    • 转移 (f1,f2) 数组判断的时候正好判断反了。应该是另一个当前选的数字和另一个集合没有交集。

    代码

    #include<bits/stdc++.h>
    using namespace std;
    #define ll long long
    const int INF = 0x3f3f3f3f,N = 505;
    inline ll read()
    {
    	ll ret=0;char ch=' ',c=getchar();
    	while(!(c>='0'&&c<='9')) ch=c,c=getchar();
    	while(c>='0'&&c<='9') ret=(ret<<1)+(ret<<3)+c-'0',c=getchar();
    	return ch=='-'?-ret:ret;
    }
    ll n,mod;
    ll dp[256][256],f1[256][256],f2[256][256];
    const int p[10]={0,2,3,5,7,11,13,17,19};
    struct node 
    {
    	int pig,stat,num;
    	inline bool operator < (const node &oth)const {return pig<oth.pig;}
    	void init()
    	{
    		int tmp=num;
    		for(int i=1;i<=8;i++)	
    			if(tmp%p[i]==0) 
    			{
    				stat|=1<<(i-1);
    				while(!(tmp%p[i])) tmp/=p[i];
    			}
    		if(tmp!=1) pig=tmp;
    	}
    }a[N];
    inline void trans(int now)
    {
    	if(a[now].pig!=a[now-1].pig||!a[now].pig||now==1)
    		for(int i=0;i<256;i++)	
    			for(int j=0;j<256;j++)	
    				f1[i][j]=f2[i][j]=dp[i][j];
    	return;
    }
    int main()
    {
    	n=read(),mod=read();
    	for(int i=1;i<=n-1;i++) a[i].num=i+1,a[i].init();
    	sort(a+1,a+n+1);
    	dp[0][0]=1LL;  
    	for(int i=1;i<=n-1;i++)	
    	{
    		trans(i);//这一段大质因数开始的时候赋值 
    		for(int j=255;j>=0;j--)
    			for(int k=255;k>=0;k--)
    			{//滚动数组,倒序枚举 
    				if(j&k) continue;
    				if(!(k&a[i].stat)) (f1[j|a[i].stat][k]+=f1[j][k])%=mod;
    				if(!(j&a[i].stat)) (f2[j][k|a[i].stat]+=f2[j][k])%=mod;
    			}
    		if(a[i].pig!=a[i+1].pig||!a[i].pig||i==n-1)//这一段大质因数结束的时候更新dp 
    		for(int j=255;j>=0;j--)
    			for(int k=255;k>=0;k--)
    			{
    				if(j&k) continue;
    				dp[j][k]=f1[j][k]+f2[j][k]-dp[j][k]+mod;//减法+mod 
    				dp[j][k]%=mod;
    			}
    	}
    	ll ans=0ll;
    	for(int i=0;i<256;i++)	
    		for(int j=0;j<256;j++)	
    		{
    			if(i&j) continue;
    			ans+=dp[i][j],ans%=mod;
    		}
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    《如何评价Kaiming He的Momentum Contrast for Unsupervised?》
    多伦多大学&NVIDIA最新成果:图像标注速度提升10倍!
    GitHub超全机器学习工程师成长路线图,开源两日收获3700+Star!
    上Github,北大、清华、浙大、中科大4大名校课程在线学,加星总数超1.8万
    使用Python+OpenCV进行图像处理(二)| 视觉入门
    重磅!刷新两项世界纪录的腾讯优图人脸检测算法DSFD开源了!
    巴黎不哭!十亿数据精准扫描,帮卡西莫多重新找回他的玫瑰花窗
    机器学习算法系列:FM分解机
    百道Python面试题实现,搞定Python编程就靠它
    学习GAN必须阅读的10篇论文
  • 原文地址:https://www.cnblogs.com/conprour/p/15487070.html
Copyright © 2011-2022 走看看