AT_abc317_f

发布时间 2023-09-07 15:28:46作者: trh0630

一、题目描述:

  给你四个整数 $N,A_1,A_2,A_3$。求满足以下条件的正整数三元组 $(X_1,X_2,X_3)$ 的数量。

  对于 $i\in [1,3],A_i\mid X_i$ 且 $X_1\oplus X_2\oplus X_3=0$。答案对 $998244353$ 取模。

  数据范围:$1\le N \le 1\times 10^{18},1\le A_1,A_2,A_3\le 10$。


 二、解题思路:

  首先我们发现 $N$ 很大,$A_i$ 很小,所以想到 $数位dp$。当然我是想不到的。

  而且这题状态也很离谱,我想了好久才想懂。解释一下 $dp[i][A][B][C][l_1][l_2][l_3][f_1][f_2][f_3]$ 的意思。

  $在二进制从底往高的第\ i\ 位中满足特定条件的组合 (X_1,X_2,X_3) 对答案做出的贡献。条件如下:$

  $X_1\ mod\ A_1 = A,X_2\ mod\ A_2 = B,X_3\ mod\ A_3 =C,l_i\ 表示\ X_i\ 是否顶格,f_i\ 表示\ X_i\ 是否为\ 0$

  好,如果理解了状态的含义,转移是简单的。只需要让每一位异或和都为\ 0\ 就行了。

  时间复杂度 $O(8\times log_2^n\times A_1\times A_2\times A_3)$。代码属实难看。


 三、完整代码:

 1 #include<bits/stdc++.h>
 2 #define M 998244353
 3 #define ll long long
 4 using namespace std;
 5 ll n,a,b,c,len,v[70];
 6 ll f[70][10][10][10][2][2][2][2][2][2];
 7 void S(ll &x,ll y){x+=y;if(x>=M)x-=M;}
 8 ll L(ll x){return x<<1;}ll R(ll x){return x<<1|1;}
 9 ll D(ll p,ll x,ll y,ll z,ll l1,ll l2,ll l3,ll f1,ll f2,ll f3){
10     if(!p) return !x&&!y&&!z&&f1&&f2&&f3;
11     if(~f[p][x][y][z][l1][l2][l3][f1][f2][f3])
12         return f[p][x][y][z][l1][l2][l3][f1][f2][f3];
13     bool A=v[p]||(!l1),B=v[p]||(!l2),C=v[p]||(!l3);
14     ll r=D(p-1,L(x)%a,L(y)%b,L(z)%c,A^1,B^1,C^1,f1,f2,f3);
15     if(A&B) S(r,D(p-1,R(x)%a,R(y)%b,L(z)%c,l1,l2,C^1,1,1,f3));
16     if(A&C) S(r,D(p-1,R(x)%a,L(y)%b,R(z)%c,l1,B^1,l3,1,f2,1));
17     if(B&C) S(r,D(p-1,L(x)%a,R(y)%b,R(z)%c,A^1,l2,l3,f1,1,1));
18     return f[p][x][y][z][l1][l2][l3][f1][f2][f3]=r;
19 } 
20 int main(){
21     ios::sync_with_stdio(false);
22     cin.tie(0);cout.tie(0);
23     cin>>n>>a>>b>>c; memset(f,-1,sizeof f);
24     while(n) v[++len]=n&1,n>>=1;
25     cout<<D(len,0,0,0,1,1,1,0,0,0)<<'\n';
26     return 0; 
27 }

四、写题心得:

  这两天写了两三道 $数位dp$ 的题,也来总结一下吧:

  $1、记忆化搜索确实好用,以后不写递推了。=> Exp++!$

  $2、一些与数字有关,数据范围又很极端的题(val=1\times 10……{18}\ 或者\ val\le 100),或许应该用\ 数位dp\ 来写。=> Exp++!$

  $3、转移不一定要\ O(1)\ 地转移,可以通过将一维数据放在转移里面来降低空间复杂度和初始化时间。(如\ P4127[AHOI2009]同类分布)=> Exp++!$