数位dp
分析:需要维护三个小于N并且分别是a1,a2,a3倍数的数,同时三个数异或的结果是0
0-N是上下限,a1,a2,a3是每个数的限制,三个数异或为0是三个数一起的限制
0-N的上下限是数位dp的基本操作,分别是a1,a2,a3的倍数也可以在过程中取模操作(在最后判断一下是否都是0)
重点在于三个数异或的结果是0
这该如何维护呢
位运算,不同于普通的数组dp,以十进制为标准进行dp,这里通过二进制进行dp
根据题意得知三数异或的结果为0,那么也就是说每一数位(二进制)上的1的个数 非0即2(异或的性质)
只有这样结果才可能是0
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<queue>
#include<vector>
#include<set>
#include<map>
using namespace std;
typedef long long LL;
#define int long long
typedef pair<int,int> PII;
const int N=1000010,mod=998244353;
int a[N];
int nn[N];
int dp[100][20][20][20][2][2][2][2][2][2];
int dfs(int u,int now1,int now2,int now3,int lim1,int lim2,int lim3,int f1,int f2,int f3 ){
if(!u){
return !now1 && !now2 && !now3 && f1 && f2 && f3;
}
if(dp[u][now1][now2][now3][lim1][lim2][lim3][f1][f2][f3]!=-1){
return dp[u][now1][now2][now3][lim1][lim2][lim3][f1][f2][f3];
}
int l1=lim1?nn[u]:1;
int l2=lim2?nn[u]:1;
int l3=lim3?nn[u]:1;
int res=0;
for(int i=0;i<=l1;i++){
for(int j=0;j<=l2;j++){
for(int k=0;k<=l3;k++){
if(i^j^k) continue;
int new1=(now1+(i<<(u-1)))%a[1];
int new2=(now2+(j<<(u-1)))%a[2];
int new3=(now3+(k<<(u-1)))%a[3];
res=(res+dfs(u-1,new1,new2,new3,lim1&&i==nn[u],lim2&&j==nn[u],lim3&&k==nn[u],f1||i,f2||j,f3||k ) ) %mod;
}
}
}
dp[u][now1][now2][now3][lim1][lim2][lim3][f1][f2][f3]=res;
return dp[u][now1][now2][now3][lim1][lim2][lim3][f1][f2][f3];
}
void solve(){
int n;
memset(dp,-1,sizeof dp);
cin>>n>>a[1]>>a[2]>>a[3];
int cnt=0;
while(n){
nn[++cnt]=n%2;
n/=2;
}
int ans=dfs(cnt,0,0,0,1,1,1,0,0,0);
cout<<ans<<endl;
}
signed main(){
int t=1;
while(t--){
solve();
}
}