Sum of Log ICPC上海区域赛 数位dp 双线程
题目大意:
思路:
这个是我第一次写的双线程的数位dp
,也是第一次碰到的需要在T组内 memset
这个 dp
数组的题目,还是很有意思的,这种题目一般都需要对 limit
进行记忆化来保证时间复杂度。
这个题目状态的定义:
dp[pos][limit1][limit2][zero]
:表示对于第pos
位,第一个的状态是limit1
,第二个的状态是limit2
,前导零的状态是zero
,这个 zero
是用来判断最高位的,也可以不用这个来判断,而是手动暴力枚举最高位是X或Y和在哪个位置。
这个题目我主要是通过看题解代码来学习掌握的,之后补一题2020CCPC网络赛 Xor 和后面ICPC济南区域赛的一个数位dp
来巩固
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod = 1e9+7;
ll dp[33][2][2][2];
// dp[pos][limit1][limit2][zero] 表示对于第pos位,第一个的状态是limit1,第二个的状态是limit2,前导零的状态是zero
// 这个zero是为了判断是不是最高位
int a[33],b[33];
ll dfs(int pos,bool limit1,bool limit2,bool zero){
// printf("pos = %d limit1 = %d limit2 = %d zero = %d
",pos,limit1,limit2,zero);
if(pos==-1) return 1;
if(dp[pos][limit1][limit2][zero]!=-1) return dp[pos][limit1][limit2][zero];
int up1 = limit1?a[pos]:1;
int up2 = limit2?b[pos]:1;
ll ans = 0;
for(int i=0;i<=up1;i++){
for(int j=0;j<=up2;j++){
if(i&j) continue;
ll res = 1;
if(!zero&&(i||j)) res = pos + 1;
ans = (ans + dfs(pos-1,limit1&&i==up1,limit2&&j==up2,zero||i||j)*res%mod)%mod;
}
}
return dp[pos][limit1][limit2][zero] = ans;
}
ll solve(){
int pos = 0,X,Y;
scanf("%d%d",&X,&Y);
memset(dp,-1,sizeof(dp));
while(X||Y){
a[pos] = X&1;
b[pos] = Y&1;
X>>=1,Y>>=1;
pos++;
}
return dfs(pos-1,true,true,false);
}
int main(){
int t;
scanf("%d",&t);
while(t--){
ll ans = solve();
printf("%lld
",(ans-1+mod)%mod);
}
return 0;
}