2020ICPC上海 C

题目链接:Problem - C - Codeforces                

题目大意:

给定XY,求\sum_{i=0}^{X} \sum_{j=[i=0]}^{Y} \left [ i\&j=0\right ] \left \lfloor\log_{2}(i+j)+1 \right \rfloor对1e9+7取模的值

其中0\leqslant X,Y\leqslant 10^{9}

思路:

直接枚举计算显然会TLE,考虑用计算二进制每一位对答案的贡献,通过观察可以发现,当i \& j=0时才会对答案有贡献,所以对于i和j,二进制下任意一位都需要满足与的结果不为1,也就是i和j二进制下任意一位都不能同时取1,这也就意味这i+j在二进制运算下并不会产生进位的情况,然后观察\left \lfloor \log_{2}(i+j)+1 \right \rfloor,由于i+j不会进位,所以\left \lfloor \log_{2}(i+j)+1 \right \rfloor的意义即为i+j的和在二进制下的位数,例如i+j为7(111)时该式的值为3,枚举每一位i和j填的数计算贡献,通过数位dp来统计。

#pragma GCC optimize(2)
#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=2e5+10;
const int mod=1e9+7;
int num1[31],num2[31];
vector<int> v;
int dp[35][2][2][2];
int cnt;
int dfs(int pos,int limit1,int limit2,int lead) {
	if(pos==0) {
		if(lead) return 0;//i+j=0不会产生贡献 
		return 1; 
	}
	if(dp[pos][limit1][limit2][lead]!=-1) {
		return dp[pos][limit1][limit2][lead];
	}
	int ans=0;
	int up1=limit1?num1[pos]:1;
	int up2=limit2?num2[pos]:1;
	for(int i=0;i<=up1;i++) {
		for(int j=0;j<=up2;j++) {
			if(i==1&&j==1) continue;//全为1的情况不会产生贡献 
			int w=-1;
			if(lead&&(i||j)) w=pos;//i+j的最高位为pos(之前有前导0的情况下且当前两个数有一个为1) 
			else w=1;//i+j已经有最高位了 
			ans=(ans+dfs(pos-1,limit1&&i==up1,limit2&&j==up2,lead&&!i&&!j)*w%mod)%mod;
		}
	}
	return dp[pos][limit1][limit2][lead]=ans;
}
void solve() {
	int x,y;
	scanf("%lld %lld",&x,&y);
	int ans=0;
	if(!x&&!y) {
		cout<<0<<endl;
		return ;
	}
	cnt=0;
	while(x||y) {
		num1[++cnt]=x%2;
		num2[cnt]=y%2;
		x/=2,y/=2;
	}
	memset(dp,-1,sizeof dp);
	printf("%lld\n",dfs(cnt,1,1,1));
}
signed main() {
	int T=1;
//	ios::sync_with_stdio(false);
//	cin.tie(0);
	cin>>T;
	while(T--) {
		solve();
	}
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值