【题解】codeforces776G Sherlock and the Encrypted Data

题目链接

题意:设x的16进制表示为xn-1xn-2...x1x0,定义h(x)=2^xn-1 or 2^xn-2 or...or 2^x1 or 2^x0。求l,l+1,...,r中满足x xor h(x)<x的数的个数。
分析:可以发现x xor h(x)<x是否成立只与h(x)的二进制表示下的最高位和x的二进制表示在该位的值有关(若为1则不等式成立)。因此以f[n][p][i]表示长度为n、16进制表示下各位数字的最大值为p、二进制表示下第i位为1的16进制数的数目,以s[n][p]表示长度为n、16进制表示下各位数字的最大值为p的16进制数的数目。则f[n][p][i]与s[n][p]均可递推预处理。
        用calc(m)计算1,2,...,m中满足x^h(x)<x的数的个数。calc(m)的计算过程如下:
        从高至底逐位扫描m的16进制表示,并统计答案。扫描至第k位时,设第k位数字为maxx且设前若干位的数字最大值为p0。枚举第k位的数字x(即此时统计前若干位与m相同,第k位数为x的这类数对答案的贡献),再枚举最后k-1位的各位数字最大值p,统计长度为k(k-1位即为长度为k)、16进制表示下各位数字的最大值为p、2进制表示下第max(p1,x,p)位为1的16进制数的数目即可(需要按max(p1,x,p)的值分类讨论)。

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
const int maxl=16;
LL s[maxl][maxl],f[maxl][maxl][maxl],px[maxl];
LL calc(LL m)
{
	if (m==-1) return 0;
	LL ret=0;
	int p0=0,maxx;
	for (int n=maxl-1;n>=0;n--)
	{
		maxx=m/px[n]&15;
		for (int x=0;x<maxx;x++)
		{
		    if (!n)
		    {
		    	int p1=max(p0,x);
		    	if (p1<4) ret+=((x&(1<<(p1-4*n)))!=0);
		    	else ret+=((m&(1<<p1))!=0);
			}
		    else
			for (int p=0;p<maxl;p++)
		    {
		    	int p1=max(p0,max(x,p));
		    	if (p1<4*n) ret+=f[n-1][p][p1];
		    	else if (p1<4*(n+1)) ret+=((x&(1<<(p1-4*n)))!=0)*s[n-1][p];
		    	else ret+=((m&(1<<p1))!=0)*s[n-1][p];
			}
		}
		p0=max(p0,maxx);
	}
	ret+=((m&(1<<p0))!=0);
	return ret;
}
int idx(char c)
{
	if (c>='0'&&c<='9') return c-'0';
	return c-'a'+10;
}
int main()
{
	px[0]=1;for (int i=1;i<maxl;i++) px[i]=px[i-1]*16;
	for (int x=0;x<maxl;x++)
	{
	    s[0][x]++;
		for (int i=0;i<4;i++)
	        if ((1<<i)&x)
	            f[0][x][i]++;
	}
	for (int n=0;n<maxl-1;n++)
	    for (int x=0;x<maxl;x++)
			for (int p=0;p<maxl;p++)
		    {
		    	int p1=max(p,x);
		    	s[n+1][p1]+=s[n][p];
		    	for (int i=0;i<min(4*(n+1),maxl);i++) f[n+1][p1][i]+=f[n][p][i];
			    if (n<=2)
				    for (int i=0;i<4;i++)
			            if ((1<<i)&x)
			                f[n+1][p1][i+4*(n+1)]+=s[n][p];
			}
	int T;cin>>T;
	while (T--)
	{
		LL l=0,r=0;
		char L[20],R[20];
		scanf("%s%s",L,R);
		int ll=strlen(L),rr=strlen(R);
		for (int i=ll-1;i>=0;i--) l+=idx(L[i])*px[ll-1-i];
		for (int i=rr-1;i>=0;i--) r+=idx(R[i])*px[rr-1-i];
		printf("%I64d\n",calc(r)-calc(l-1));
	}
	return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值