ABC317F题解

前言

很遗憾没能 场切6题 。但是个人认为这题不适合放 ABC 的 F。

这个题解基本是按照代码来讲的,所以只能凑合看。且本文的 DP 是按照记忆化搜索的形式写的。

有不理解的或者有笔误可以评论或私信。

正文

让人头大的数位 DP。建议评蓝。

将三个数二进制拆分,使三个数异或为 \(0\) 相当于每个二进制位三个数中有 \(0\)\(2\) 个是 \(1\)

所以考虑数位 DP,设 \(dp[i][m1][m2][m3][lim1][lim2][lim3]\) 为第 \(i\) 位,三个数模 \(A1\)\(A2\)\(A3\) 分别是 \(m1\)\(m2\)\(m3\),以及三个数有没有最高位限制。

这道题比较特殊。仔细想想可以发现这道题最高位限制 必须放状态里。不然 TLE。

下文设 \(S[i]\)\(N\) 的第 \(i\) 位二进制。

马上烧脑起来了。可以结合代码理解。公式比较乱请谅解。

一个状态 \(dp[i][m1][m2][m3][lim1][lim2][lim3]\) 可以如此转移过来:

首先,讨论 三个数的第 \(i\) 位全部为 \(0\) 的情况。

\(S[i]=1\)\(dp[i-1][m1][m2][m3][0][0][0]\) 可以对状态产生贡献。

\(S[i]=0\)\(dp[i-1][m1][m2][m3][lim1][lim2][lim3]\) 可以对状态产生贡献。

其实就是最高位限制的不同。

然后考虑 有两个是 \(1\) 的情况。

有三种情况,这里以 \(X1\)\(X2\) 的第 \(i\) 位为 \(1\) 为例。

\(S[i]=1 \vee (lim1=0 \wedge lim2=0)\)

\(dp[i-1][(m1+2^i)\bmod A1][(m2+2^i)\bmod A2][m3][lim1\wedge S[i]=0][lim2][lim3]\) 可以贡献。

可以想想为什么是这样。另外两个情况,同理。

最核心的 DP 转移到这里就完了。还没完,我们又发现我们还要 减去三个数中有数为 \(0\) 的情况。

为此还要再跑一次数位 DP 专门处理。枚举 \(X1\)\(X2\)\(X3\)\(0\) 的情况。

举个例子,现在考虑的是 \(X3\)\(0\),那么就设 \(dp2[i][m1][m2]\) 为第 \(i\) 位,\(X1\)\(X2\)\(A1\)\(A2\)\(m1\)\(m2\) 的情况。

这个 DP 很简单,并没有分类讨论。

\(dp2[i][m1][m2]=dp2[i-1][m1][m2]+dp2[i-1][(m1+2^i)\bmod A1][(m2+2^i)\bmod A2]\)

如果考虑的是 \(X1\) 或者 \(X2\)\(0\),还是同理 DP 即可。

像这样跑三次就可以求出三个数中有数为 \(0\) 的不合法贡献。

最后还有一个细节就是这样会把 三个数全 \(0\) 的情况减三次,加回去 \(2\) 即可。

code

希望各位看得来。

//writer:Oier_szc

#include <bits/stdc++.h>
#define TS puts("I AK IOI");
#define int long long
using namespace std;
const int N=2005,mod=998244353;
int n,a1,a2,a3,ans=0;
int dp[70][15][15][15][2][2][2];
int S[70],yes[70],len=-1;
int dfs(int deep,int m1,int m2,int m3,bool lim1,bool lim2,bool lim3)
{
	if(deep==-1) return m1==0&&m2==0&&m3==0;
	if(dp[deep][m1][m2][m3][lim1][lim2][lim3]!=-1) return dp[deep][m1][m2][m3][lim1][lim2][lim3];
	int res=0;
	if(S[deep]) res=(res+dfs(deep-1,m1,m2,m3,0,0,0))%mod;
	else res=(res+dfs(deep-1,m1,m2,m3,lim1,lim2,lim3))%mod;
	if(S[deep]||(!lim2&&!lim3)) res=(res+dfs(deep-1,m1,(m2+(1ll<<deep))%a2,(m3+(1ll<<deep))%a3,lim1&&!S[deep],lim2,lim3))%mod;
	if(S[deep]||(!lim1&&!lim3)) res=(res+dfs(deep-1,(m1+(1ll<<deep))%a1,m2,(m3+(1ll<<deep))%a3,lim1,lim2&&!S[deep],lim3))%mod;
	if(S[deep]||(!lim1&&!lim2)) res=(res+dfs(deep-1,(m1+(1ll<<deep))%a1,(m2+(1ll<<deep))%a2,m3,lim1,lim2,lim3&&!S[deep]))%mod;
	dp[deep][m1][m2][m3][lim1][lim2][lim3]=res;
	return res;
}
int dp2[70][15][15],M1,M2;
int dfs2(int deep,int m1,int m2,int lim)
{
	if(deep==-1) return m1==0&&m2==0;
	if(dp2[deep][m1][m2]!=-1&&!lim) return dp2[deep][m1][m2];
	int up=lim?S[deep]:1ll,res=0;
	for(int i=0;i<=up;++i)
	{
		res+=dfs2(deep-1,(m1+(i<<deep))%M1,(m2+(i<<deep))%M2,lim&&i==up);
	}
	if(!lim) dp2[deep][m1][m2]=res;
	return res;
}
signed main()
{
	scanf("%lld%lld%lld%lld",&n,&a1,&a2,&a3);
	while(n)
	{
		S[++len]=n%2;
		n/=2;
	}
	int cnt_0=-2;
	M1=a1,M2=a2;
	memset(dp2,-1,sizeof(dp2));
	cnt_0=(cnt_0+dfs2(len,0,0,1))%mod;
	M1=a1,M2=a3;
	memset(dp2,-1,sizeof(dp2));
	cnt_0=(cnt_0+dfs2(len,0,0,1))%mod;
	M1=a2,M2=a3;
	memset(dp2,-1,sizeof(dp2));
	cnt_0=(cnt_0+dfs2(len,0,0,1))%mod;
	memset(dp,-1,sizeof(dp));
	printf("%lld\n",(dfs(len,0,0,0,1,1,1)-cnt_0+mod)%mod);
	return 0;
}
posted @ 2023-08-27 22:16  Oier_szc  阅读(81)  评论(0)    收藏  举报