题解 CF1710C XOR Triangle
我们令 \(x=a\ \text{xor}\ b\),\(y=b\ \text{xor}\ c\),\(z=c\ \text{xor}\ a\)。容易发现 \(x,y,z\) 满足 \(x\ \text{xor}\ y\ \text{xor}\ z=0\)。我们考虑他们什么时候可以构成三角形。结论是:当且仅当存在其中两个数 \(x,y\) 满足 \(x\ \text{and}\ y=0\),不满足构成三角形。下面简单说明一下。
- 充分:当 \(x\ \text{and}\ y=0\) 时,\(x\) 与 \(y\) 不会同时有一位是 \(1\)。所以 \(x\ \text{xor}\ y=x+y=z\),因此不满足。
- 必要:任意两个数的与都不等于 \(0\),我们不妨三个数大小顺序 \(x>y>z\)。由于 \(y\ \text{and}\ z>0\),那么 \(y,z\) 至少有一位同时是 \(1\),所以 \(x=y\ \text{xor}\ z<y+z\)。也就满足构成三角形。
于是我们考虑数位 DP。从高位到低位做,记三个变量表示当前 \(a,b,c\) 是否满足已填的位都与 \(n\) 相同的限制,再记三个变量分别表示 \(x\ \text{and}\ y\),\(y\ \text{and}\ z\),\(z\ \text{and}\ x\) 是否仍为 \(0\)。转移是平凡的,枚举 \(a,b,c\) 这一位填了什么,更新上述六个变量即可。
这样做的复杂度是 \(O(2^9n)\)。
const int N=200005;
int n,m;
char s[N];
ll dp[N][2][2][2][2][2][2];
ll dfs(int u,bool a,bool b,bool c,bool x,bool y,bool z)
{
if (u==n+1)
{
if (x&&y&&z) return 1;
return 0;
}
if (dp[u][a][b][c][x][y][z]!=-1) return dp[u][a][b][c][x][y][z];
ll res=0;
for (int i=0;i<=1;i++)
{
if (a&&i>s[u]-'0') continue;
for (int j=0;j<=1;j++)
{
if (b&&j>s[u]-'0') continue;
for (int k=0;k<=1;k++)
{
if (c&&k>s[u]-'0') continue;
res=(res+dfs(u+1,a&(s[u]-'0'==i),b&(s[u]-'0'==j),c&(s[u]-'0'==k),x|((i^j)&(j^k)),y|((i^k)&(j^k)),z|((i^k)&(j^i))))%mod;
}
}
}
dp[u][a][b][c][x][y][z]=res;
return res;
}
int main()
{
cin >> (s+1);
n=strlen(s+1);
memset(dp,-1,sizeof(dp));
cout << dfs(1,1,1,1,0,0,0);
return 0;
}