声明:
思路和板子都来源于异或三角
https://blog.youkuaiyun.com/a12377978/article/details/124893215#comments_26867660
上面那篇帖子写的已经比较详细了,但是可能由于答主对二进制的思维实在不敏锐,所以有些点想了很久才相通,希望对以上帖子进行补充,在帮助大家的同时,加强一下我自己的理解。
思路:
性质1.由条件二知道,a,b,c互不相等,且
我们假设把a、b、c转为二进制表示后分别填入第1、2、3行。根据 三个数每一位按位异或都要为0,那么就是每一列只能有两种情况,即 两个1或者三个0。
1 1 1 0 1 1 ... ... 1 0 1 0 0 1 ... ... 0 1 1 1 1 0 ... ...
性质二:状态转移
假如a和b确定了,那么c=a^b。所以我们只需要枚举a、b的每一位即可。
我们假设a是三角形的最长边,bc是短边,在枚举完结果后答案乘以3就是本题答案。
从高到低位枚举,必须满足以下条件:
(0,1)在(1,0)之后出现,为了a>b
(0,1)在(1,1)之后出现,为了a>c
这两种情况应该比较好想,目的都是为了保证a是最大的,不理解的可以画图。或者去看一下这篇博客
(16条消息) 【蓝桥刷题】备战国赛——异或三角_DAY Ⅰ的博客-优快云博客
在枚举完ab的每一位后,必然存在至少一次的状态为(0,1)------这个条件感觉他们都没说清楚,目的是为了保证满足题意,即abc组成三角形
首先我们知道,异或运算也叫做二进制不进位加法|
这里我们要满足a<b+c三角形的规则
a<b+c ----> b^c<b+c
因为b^c相当于二进制的不进位加法
我们把左右两边都先转化为二进制后做加法
左边如果出现11那么本位不进位之间变成0,右边进1变为0.
如果有b^c<b+c,那么一定至少存在一次bc的状态为11
我们就可以得出结论 ab必然存在至少一次的状态为(0,1)
性质三:状态压缩
我们用到的状态为01,10,11,这是第一次状态压缩。
再做状态压缩,用三位二进制分别表示三种状态是否出现过 111(第一个1表示状态11已经出现过,第二个1表示状态10出现过,第三个1表示状态01出现过)
代码
#include<iostream> #include<cstring> using namespace std; typedef long long LL; int nums[32]; LL f[32][2][8]; LL dfs(int u,int limit,int state){ if(!u)return state==7;//如果走完 LL &t=f[u][limit][state]; if(~t)return t;//如果f[u][limit][state]计算过 LL res=0; int up=limit?nums[u]:1; for(int i=0;i<=up;i++){ if(!i){//a的当前位取0 // 0 0 res+=dfs(u-1,limit&i==up,state); if(state>=6)//在这个地方做限制 //0 1 我们必须保证1 1和1 0已经出现过即110=6 res+=dfs(u-1,limit&i==up,state|1); } else{ // 1 0 ->010 res+=dfs(u-1,limit&i==up,state|2); // 1 1 ->100 res+=dfs(u-1,limit&i==up,state|4); } } return t=res; } LL dp(int x){ int cnt=0; memset(f,-1,sizeof f); while(x)nums[++cnt]=x&1,x>>=1; return dfs(cnt,1,0)*3; } int main(void){ int T; scanf("%d",&T); while(T--){ int n; scanf("%d",&n); printf("%lld\n",dp(n)); } return 0; }
结语:
大家蓝桥杯国赛一起加油!!!