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;
}

浙公网安备 33010602011771号