学习笔记15:(AtCoder Beginner Contest 317 F)

F - Nim (atcoder.jp)

数位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();
    }
}
        

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值