首页 > 其他分享 >p2150-solution

p2150-solution

时间:2024-03-01 09:12:02浏览次数:25  
标签:dpW p2150 cup int S2 S1 solution dp

P2150 Solution

link

首先两人选的数两两互质相当于两人的质因数集合无交。

先考虑 \(n\le 30\):由于 \(30\) 内的质因只有 \(10\) 个,我们考虑状压 \(dp\)。

设 \(dp_{i,S1,S2}\) 表示考虑到第 \(i\) 个数,G 选了质因数集合 \(S1\),W 选了质因数集合 \(S2\) 的方案数。

刷表转移:

\[dp_{i,S1\cup k,S2}\gets dp_{i,S1\cup k,S2}+dp_{i-1,S1,S2}(k\cap S2=\varnothing) \]

\[dp_{i,S1,S2\cup k}\gets dp_{i,S1,S2\cup k}+dp_{i-1,S1,S2}(k\cap S1=\varnothing) \]

其中 \(k\) 表示第 \(i\) 个数的质因数集合。

这里可以滚掉第一维,复杂度 \(\mathcal O(2^{20}n)\)。


接下来是 \(n\le 500\):

我们发现一个数 \(x\) 最多有一个 \(>\sqrt x\) 的大质因子(可能没有),那么 \(\le 500\) 的数就最多有一个 \(>22\) 的大质因子。考虑一开始先预处理出每个数的大质因子,剩下的小质因子状压处理。

剩下的小质因子只剩下了 \(8\) 个,也就是说我们的状态数只剩下了 \(2^8\)!

我们将所有数按照它的大质因子排序,这样相同大质因子的数就会在一个连续段中,这些数只能由一个人取。我们在每个连续段内进行dp:

设 \(dpG_{i,S1,S2}\) 考虑到第 \(i\) 个数,\(i\) 所在连续段的数由 G 取,G 选了小质因数集合 \(S1\),W 选了小质因数集合 \(S2\) 的方案数。\(dpW_{i,S1,S2}\) 同理,\(dp_{i,S1,S2}\) 的定义不变。

转移:

\[dpG_{i,S1\cup k,S2}\gets dpG_{i,S1\cup k,S2}+dpG_{i-1,S1,S2}(k\cap S2=\varnothing) \]

\[dpW_{i,S1,S2\cup k}\gets dpW_{i,S1,S2\cup k}+dpW_{i-1,S1,S2}(k\cap S1=\varnothing) \]

在每个连续段开头,我们把 \(dpG,dpW\) 都赋值为 \(dp\);在每个连续段结尾,我们需要由 \(dpG,dpW\) 处理出 \(dp\):

\[dp_{r,S1,S2}=dpG_{r,S1,S2}+dpW_{r,S1,S2}-dp_{l-1,S1,S2} \]

其中 \(l,r\) 分别表示连续段的左右端点。减掉最后一项是因为两人都不选大质因子的情况算了两遍。

我们再把第一维都滚掉,最后复杂度就是 \(\mathcal O(2^{16}n)\)。

当然我们这里优化一下:由于 \(S1\cap S2=\varnothing\),我们在枚举 \(S1,S2\) 的时候可以先枚举 \(S1\),再枚举 \(S1\) 的补集的子集。这样复杂度是 \(\mathcal O(3^8n)\)的。

代码

\(\mathcal O(2^{16}n)\)

using namespace std;
const int N = 1 << 8 | 7;
const int inf = ~0u >> 2;
int pi[] = {0 , 2 , 3 , 5 , 7 , 11 , 13 , 17 , 19};
struct data
{
	int val,s;
	int bf; // biggest factor
	friend bool operator < (data a , data b)
		{return a.bf < b.bf;}
	void split()
	{
		int x = val;
		for(int i = 1;i <= 8;i++)
			if(x % pi[i] == 0)
			{
				s |= 1 << i - 1;
				while(x % pi[i] == 0)
					x /= pi[i];
			}
		if(x != 1)
			bf = x;
	}
}a[5005];
int n,p;
int dp[N][N];
int dpG[N][N],dpW[N][N];
int main()
{
	cin >> n >> p;
	for(int i = 1;i <= n - 1;i++)
		a[i].val = i + 1,a[i].split();
	sort(a + 1 , a + n);
	dp[0][0] = 1;
	a[0].bf = a[n].bf = 114514;
	for(int i = 1;i <= n - 1;i++)
	{
		if(a[i].bf != a[i - 1].bf || !a[i].bf)
			for(int s1 = 0;s1 < 256;s1++)
				for(int s2 = 0;s2 < 256;s2++)
					dpG[s1][s2] = dpW[s1][s2] = dp[s1][s2];
		for(int s1 = 255;~s1;s1--)
			for(int s2 = 255;~s2;s2--)
				if( !(s1 & s2) )
				{
					if( !(s2 & a[i].s) )
						dpG[s1 | a[i].s][s2] = ( dpG[s1 | a[i].s][s2] + dpG[s1][s2] ) % p;
					if( !(s1 & a[i].s) )
						dpW[s1][s2 | a[i].s] = ( dpW[s1][s2 | a[i].s] + dpW[s1][s2] ) % p;
				}
		if(a[i].bf != a[i + 1].bf || !a[i].bf)
			for(int s1 = 0;s1 < 256;s1++)
				for(int s2 = 0;s2 < 256;s2++)
					dp[s1][s2] = (0ll + dpG[s1][s2] + dpW[s1][s2] - dp[s1][s2] + p) % p;
	}
	int ans = 0;
	for(int s1 = 0;s1 < 256;s1++)
		for(int s2 = 0;s2 < 256;s2++)
			if( !(s1 & s2) )
				ans = ( ans + dp[s1][s2] ) % p;
	cout << ans << endl;
    return 0;
}

\(\mathcal O(3^8n)\)

using namespace std;
const int N = 1 << 8 | 7;
const int inf = ~0u >> 2;
int pi[] = {0 , 2 , 3 , 5 , 7 , 11 , 13 , 17 , 19};
struct data
{
	int val,s;
	int bf; // biggest factor
	friend bool operator < (data a , data b)
		{return a.bf < b.bf;}
	void split()
	{
		int x = val;
		for(int i = 1;i <= 8;i++)
			if(x % pi[i] == 0)
			{
				s |= 1 << i - 1;
				while(x % pi[i] == 0)
					x /= pi[i];
			}
		if(x != 1)
			bf = x;
	}
}a[5005];
int n,p;
int dp[N][N];
int dpG[N][N],dpW[N][N];
int main()
{
	cin >> n >> p;
	for(int i = 1;i <= n - 1;i++)
		a[i].val = i + 1,a[i].split();
	sort(a + 1 , a + n);
	dp[0][0] = 1;
	a[0].bf = a[n].bf = 114514;
	for(int i = 1;i <= n - 1;i++)
	{
		if(a[i].bf != a[i - 1].bf || !a[i].bf)
			for(int s1 = 255,t = 0;~s1;s1--,t = ~s1 & 255)
				for(int s2 = t;~s2;s2 = s2 ? s2 - 1 & t : -1)
					dpG[s1][s2] = dpW[s1][s2] = dp[s1][s2];
		for(int s1 = 255,t = 0;~s1;s1--,t = ~s1 & 255)
			for(int s2 = t;~s2;s2 = s2 ? s2 - 1 & t : -1)
			{
				if( !(s2 & a[i].s) )
					dpG[s1 | a[i].s][s2] = ( dpG[s1 | a[i].s][s2] + dpG[s1][s2] ) % p;
				if( !(s1 & a[i].s) )
					dpW[s1][s2 | a[i].s] = ( dpW[s1][s2 | a[i].s] + dpW[s1][s2] ) % p;
			}
		if(a[i].bf != a[i + 1].bf || !a[i].bf)
			for(int s1 = 255,t = 0;~s1;s1--,t = ~s1 & 255)
				for(int s2 = t;~s2;s2 = s2 ? s2 - 1 & t : -1)
					dp[s1][s2] = (0ll + dpG[s1][s2] + dpW[s1][s2] - dp[s1][s2] + p) % p;
	}
	int ans = 0;
	for(int s1 = 255,t = 0;~s1;s1--,t = ~s1 & 255)
		for(int s2 = t;~s2;s2 = s2 ? s2 - 1 & t : -1)
			ans = ( ans + dp[s1][s2] ) % p;
	cout << ans << endl;
    return 0;
}

标签:dpW,p2150,cup,int,S2,S1,solution,dp
From: https://www.cnblogs.com/iorit/p/18046093

相关文章

  • p1587-solution
    P1587Solutionlink给你\(n,m,k\),求满足\(1\lex\len,1\ley\lem\)且最简分数\(\dfracxy\)是\(k\)进制下纯循环小数的二元组\((x,y)\)个数。考虑纯循环小数的性质:我们知道纯循环小数的小数部分去除一个循环节后与原数的循环节相等,也就是\[\fracxy-\left\lfloor......
  • 2.29闲话 & solution 『任无数/花开花落之后/你仍君临芸芸众生』
    明天就省选了,我咋啥也不会最破防的一集一道LCT调了一下午没调出来最后发现是min和max取反了改完之后发现自己的访问有问题,如果有0边权我就会直接返回LLONG_MAX啊哈哈,我调了一下午,发了7个帖子然后校内OJ过了,POJ因为只有C++98所以CE了哈哈哈写个简要题解吧[......
  • P10202 [湖北省选模拟 2024] 沉玉谷 Solution
    好像比题解劣一个\(n\),但是也跑的很快。首先说明,问题等价于计算有多少种本质不同的方案使得整个序列被删完,证明省略。考虑用区间的方式表述这些操作,具体的,忽略删除后的移位操作,将每次删除的左右段点视为一个区间,则一定会有:区间的并是\([1,n]\)。区间之间要么不交,要么包含。......
  • solution-P1667(more)
    这道题,我不会做,我就是菜,我就是没水平,我就是傻逼。这道题,我不会做,我就是菜,我就是没水平,我就是傻逼。这道题,我不会做,我就是菜,我就是没水平,我就是傻逼。正文直接前缀和,发现操作相当交换\(s_{i-1},s_j\),显然最后我们只需要让\(s\)单调上升即可。直接做,找有多少个环,答案为\(n......
  • niu-zi-solution
    牛子Solutionlink结论:一个方案合法当且仅当,每行数种类为\(2\),或者每列数种类为\(2\)。证明:我们试图证明,如果一个方案存在一行的数种类\(\ge3\),则这个方案的每列数种类为\(2\)。对于有\(\ge3\)种数的这一行,必然存在某连续的三个数两两不同,即形如abc的形式。这个可以......
  • cf1748f-solution
    CF1748FSolutionlink题目也就是要我们交换每对\(a_i\)和\(a_{n-1-i}\)。考虑如何利用这个异或操作交换:我们自然地想到x^=y,y^=x,x^=y。如何操作使得x^=y?我们把环上\(x\)到\(y\)的路径拉出来,假装是个序列:\(a_x.a_{x+1},a_{x+2},\dots,a_{y-2},a_{y-1},a_y\)现在要使......
  • cf1621g-solution
    CF1621GSolutionlink考虑对每个位置\(i\)作为\(i_j\)时计算贡献。\(i\)对一次答案有贡献当且仅当:设其子序列内最右端的位置为\(r\),则要满足\(r\)右侧存在一个数大于\(a_{i}\)。即,设\(lst_i\)表示整个序列最右侧的大于\(a_i\)的数,要满足\(lst_i>r\)。现......
  • cf1606e-solution
    CF1606ESolutionlink考虑dp。注意到这个题造成的伤害与剩余人数有关,每次消灭的人数又与剩余人的血量最大值有关:设\(dp_{i,j}\)表示剩下\(i\)个人中血量最大值为\(j\)的方案数。显然当\(i-1>=j\)时一次伤害就可以杀光所有人,于是这时\(dp_{i,j}=j^i-(j-1)^i\)(只需让......
  • cf1599j-solution
    CF1599JSolutionlink题意:给你一个长为\(n\)的序列\(b\),请你构造一个长为\(n\)的序列\(a\),满足\(b\)中的数都能由\(a\)中两个不同下标的数相加得到。无解报告NO,\(n\le10^3,b_i\le10^6\)。我们发现如果我们能用\(a_1\sima_m\)来凑出\(b_1\simb_m\),剩下的令......
  • cf1583h-solution
    CF1583HSolutionlink第一问容易处理,将边权从大到小排序,并查集维护一下连通块最大值简单搞搞就好。考虑第二问。我们对原树,按照\(t\)的权值建造克鲁斯卡尔重构树,那么两点的lca权值即它们路径上边权最大值。同样按照边权\(c\)从大到小将边排序,维护连通块内最大值的同时,维......