[2020 ICPC Shanghai Site] - C. Sum of Log (数位dp)

题目

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<=Xj<=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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值