[题解]P9387 [THUPC 2023 决赛] 巧克力
参考:浅谈数位 DP,从入门到入土 ChatSheep。笔记质量很高,感谢原作者。
由于还没学博弈论,所以就直接跳到转化后的题意了。
给定 \(n,m\),令 \(x=1\oplus\dots\oplus n\oplus m\)。求满足下列条件的三元组 \((a,b,c)\) 数量:
\(a+b+c\le n\) 或 \(a+b+c=m\)(若都满足,算 \(2\) 次)。
\((a+b+c)\oplus a\oplus c=x\)。
\(a,c\ge 0,b>0\)。
考虑数位 DP。
因为出现了求和,所以需要处理进位的问题,常见的套路是从低位向高位枚举。
具体来说,在搜索过程中记录 \(f[p][carry][limn][eqm][zrob]\)。
-
\(p\):当前正在填写 \(a,b,c\) 的哪一位(从最低位即第 \(0\) 位开始)。
-
\(carry=0/1/2\):\(p\) 对 \(p+1\) 的进位数。
-
\(limn=0/1/2\):\(a+b+c\) 的低 \(p\) 位与 \(n\) 的低 \(p\) 位的大小关系(\(<,=,>\))。
-
\(eqm=0/1\):\(a+b+C\) 的低 \(p\) 位是否与 \(m\) 的低 \(p\) 位相同。
-
\(zrob=0/1\):\(b\) 的低 \(p\) 位是否为 \(0\)。
转移时枚举 \(a,b,c\) 的第 \(p\) 位,在满足第二条的情况下继续搜索。
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=64,P=1e9+7;
int t,n,m,x,f[N][3][3][2][2],ta[N],tb[N],tc[N];
bitset<N> a;
int dfs(int p,int carry,int limn,bool eqm,bool zrob){//limn=0/1/2 </=/>
if(p==64) return (!carry&&!zrob)*(eqm+(limn<=1));
if(~f[p][carry][limn][eqm][zrob]) return f[p][carry][limn][eqm][zrob];
int ans=0;
bool ndig=(n>>p)&1,mdig=(m>>p)&1,xdig=(x>>p)&1;
for(int a=0;a<2;a++) for(int b=0;b<2;b++) for(int c=0;c<2;c++){
bool d=(a+b+c+carry)&1;
ta[p]=a,tb[p]=b,tc[p]=c;
if((d^a^c)==xdig) ans+=dfs(p+1,(a+b+c+carry)>>1,d<ndig?0:d==ndig?limn:2,eqm&&d==mdig,zrob&&!b);
}
return f[p][carry][limn][eqm][zrob]=ans%P;
}
signed main(){
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
cin>>t;
while(t--){
cin>>n>>m;
x=m^((n&3)==0?n:(n&3)==1?1:(n&3)==2?n+1:0);
memset(f,-1,sizeof f);
cout<<dfs(0,0,!n,1,1)<<"\n";
}
return 0;
}
浙公网安备 33010602011771号