[P8766 [蓝桥杯 2021 国 AB] 异或三角]题解-数位DP
P8766 [蓝桥杯 2021 国 AB] 异或三角
分析
题目中给出了三个限制
首先我们不妨设\(a,b \lt c\),则
而由于我们把\(c\)作为了最大值,原题需要有序对\((a,b,c)\)
所以\(ans \ast 3\)
1.\(1 \leq a,b,c \leq n\)
2.\(a \oplus b \oplus c=0\)
3.\(a+b \gt c\)
而在枚举过程中,我们只需要保证\(c \lt n\)即可
由2可知:\(a \oplus b=c\)则
\(a+b \gt a \oplus b\)
这样就得到了需要满足的三个条件:
1.\(a\le c\)
2.\(b\le c\)
3.\(a \oplus b \lt a+b\)而这种情况只有\(a=1\)且\(b=1\)的情况出现后才满足
而对于任意一位,满足条件的只有四组\((a,b,c)\)
\((1,1,0),(0,0,0),(1,0,1),(0,1,0)\)
我们就可以只对\(c\)进行限制将三种状态状压来进行数位dp
可以令\(flag \lt 8\)
它的每一位分别表示我们所要的限制即可
代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
int n,T,a[50],len=0;
ll dp[50][10][2];
//当前枚举到第id位,是否满足三个状态flag,c是否达到上限limit
ll dfs(int id,int flag,int limit){
//1.出口
if(id==0) return flag==7;//满足所有条件
if(dp[id][flag][limit]!=-1) return dp[id][flag][limit];
//2.能做的事情
int bound= limit==1 ?a[id]:1;
ll res=0;
for(int i=0;i<=bound;i++){
if(i==0){//c=0 对应 (0,0,0),(1,1,0)
int a=flag&1,b=flag>>1 &1;//判断a<c,b<c
res+=dfs(id-1,flag,limit&&i==bound);//(a,b,c)=(0,0,0)
if(a&&b) res+=dfs(id-1,flag|4,limit&&i==bound); //(a,b,c)=(1,1,0)
}
if(i==1){//c=0 对应 (1,0,1),(0,1,1)
res+=dfs(id-1,flag|2,limit&&i==bound);//(a,b,c)=(1,0,1)满足b<c
res+=dfs(id-1,flag|1,limit&&i==bound);//(a,b,c)=(0,1,1)满足a<c
}
}
return dp[id][flag][limit]=res;
}
ll solve(int x){
len=0;
while(x){
a[++len]=x%2;
x/=2;
}
memset(dp,-1,sizeof(dp));
return dfs(len,0,1);
}
int main(){
/*2023.4.22 hewanying P8766 [蓝桥杯 2021 国 AB] 异或三角 数位dp*/
scanf("%d",&T);
while(T--){
scanf("%d",&n);
printf("%lld\n",solve(n)*3);
}
return 0;
}
总结
1.在我们特定顺序时,一定要注意题目给的是有序对还是无序对,要进行相应的转换
2.定了\(a,b,c\)的大小关系时,只需要判断最大的小于limit即可
3.多种状态可以采用状压来存储