题目
https://codeforces.com/gym/102900/problem/C
思路
分析一下那个式子,看成二进制讨论每个有贡献的数对(i,j)
就好说了:
对于一个数对 (i,j)
,满足以下条件
0 <= i <= X
0 <= j <= Y
i & j == 0
,也就是二进制码中没有它们者同时是1
的位。
那么就会对答案有贡献,贡献是 log2(i+j) +1
,鉴于 i&j==0
,于是有 (i+j) == (i|j)
,也就是说,这个贡献值就是 i j 或起来的最高位的位数,即 i j 的最高位 1 的位数。
此时,就想到对于每种贡献,讨论它的次数,即贡献为 k 的 (i,j)
数对个数。
考虑数位 dp,由低往高推。 还有一种理解方法,就是定下 i|j
第 k 位是1,更高的位都是零,低位01随意,若是1,分配给 i 或者 j,注意同时满足 i<=X
和 j<=Y
。
令 f[a][b][c]
abc分别代表ij都是 a 位数(可以有前导零); i 是否大于 X 对应的那 a 个位组成的数; j 是否大于 Y 对应的那 a 个位组成的数。 值代表这样的 ij 对个数。
转移方程可能我的写法有点麻烦了……不过思路也很清楚,考虑XY对应当前位置的 01,以及考虑当前位置放 1 还是 0即可。
可以再看看代码理解一下吧。
时间复杂度大概就是 O(n logn) 吧。
代码
#include <bits/stdc++.h>
#define Ha 1000000007
using namespace std;
//X>=Y
void solve(long long X, long long Y)
{
long long ans=0,cnt,tmp;
long long f[35][2][2]={
0};
//f[a][b][c] 前 a 位,i 大于X ?,j大于Y ?
f[0][0][0]=1;
for (long long a=1; a<32; a++)
{
//(0,0) (1,0) (0,1)
tmp=1ll<<(a-1);
if (tmp>X && tmp>Y)
{
break;
}
//f[a][0][0]
if (X&tmp && Y&tmp)
f[a][0][0]= (f[a-1][0][0]+f[a-1][1][0]+f[a-1][0][1]+f[a-1][1][1]) + (f[a-1][0][0]+f[a-1][0][1]) + (f[a-1][0][0]+f[a-1][1][0]);
else if (X&tmp)
f[a][0][0]= (f[a-1][0][0]+f[a-1][1][0]) + (f[a-1][0][0]) +(0);
else if (Y&tmp)
f[a][0][0]= (f[a-1][0][0]+f[a-1][0][1]) + (0) + (f[a-1][0][0]);
else
f[a][0][0]= (f[a-1][0][0]);
f[a][0][0]%=Ha;
//f[a][0][1]
if (X&tmp && Y&tmp)
f[a][0][1]= (0) + (0) + (f[a-1][0][1]+f[a-1][1][1]);
else if (X&tmp)
f[a][0][1]= (f[a-1][0][1]+f[a-1][1][1]) + (f[a-1][0][1]) + (f[a-1][0][0]+f[a-1][1][0]+f[a-1][0][1]+f[a-1][1][1]);
else if (Y&tmp)
f[a][0][1]= (0) + (0) + (f[a-1][0][1]);
else
f[a][0][1]= (f[a-1][0][1]) + (0) + (f[a-1][0][0]+f[a-1][0][1]);
f[a][0][1]%=Ha;
//f[a][1][0]
if (X&tmp && Y&tmp)
f[a][1][0]= (0) + (f[a-1][1][0] + f[a-1][1][1]) + (0);
else if (X&tmp)
f[a][1][0]= (0) + (f[a-1][1][0]) + (0);
else if (Y&tmp)
f[a][1][0]= (f[a-1][1][0]+f[a-1][1][1]) + (f[a-1][0][0]+f[a-1][1][0]+f[a-1][0][1]+f[a-1][1][1]) + (f[a-1][1][0]);
else
f[a][1][0]= (f[a-1][1][0]) + (f[a-1][0][0]+f[a-1][1][0]) + (0);
f[a][1][0]%=Ha;
//f[a][1][1]
if (X&tmp && Y&tmp)
f[a][1][1]= (0) + (0) + (0);
else if (X&tmp)
f[a][1][1]= (0) + (f[a-1][1][1]) +(0);
else if (Y&tmp)
f[a][1][1]= (0) + (0) + (f[a-1][1][1]);
else
f[a][1][1]= (f[a-1][1][1]) + (f[a-1][0][1]+f[a-1][1][1]) + (f[a-1][1][0]+f[a-1][1][1]);
f[a][1][1]%=Ha;
}
for (long long a=1; a<32; a++)
{
tmp=1ll<<(a-1);
cnt=0;
//(1,0)
if (X/tmp==1)
{
cnt+=f[a-1][0][0];
if (Y/tmp>0)
cnt+=f[a-1][0][1];
}
else if (X/tmp>1)
{
cnt+=f[a-1][1][0]+f[a-1][0][0];
if (Y/tmp>0)
cnt+=f[a-1][1][1]+f[a-1][0][1];
}
//(0,1)
if (Y/tmp==1)
{
cnt+=f[a-1][0][0];
if (X/tmp>0)
cnt+=f[a-1][1][0];
}
else if (Y/tmp>1)
{
cnt+=f[a-1][0][1]+f[a-1][0][0];
if (X/tmp>0)
cnt+=f[a-1][1][1]+f[a-1][1][0];
}
if (cnt==0)
{
break;
}
ans+=cnt%Ha*a%Ha;
}
printf("%lld\n",ans%Ha);
}
int main()
{
long long T,X,Y;
scanf("%lld",&T);
while (T--)
{
scanf("%lld%lld",&X,&Y);
if (X<Y)
solve(Y,X);
else
solve(X,Y);
}
return 0;
}