UVA11645 - Bits

链接

https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem&problem=2692

题解

数位 d p dp dp
g i g_i gi表示前面已经填的数字不对这一位产生限制的前提下,可以含有前导 0 0 0 i i i位数字的相邻 1 1 1数目之和是多少
不管第 i i i位填 0 0 0还是 1 1 1,前面总要从 0 0 0循环到 2 i − 1 − 1 2^{i-1}-1 2i11,只有当这一位和前一位都填 0 0 0的时候,这一位才会对答案有贡献,所以 g i = 2 g i − 1 + 2 i − 2 g_i=2g_{i-1}+2^{i-2} gi=2gi1+2i2
f i f_i fi表示前面对这一位数字产生限制的前提下,可以含有前导 0 0 0 i i i位数字的相邻 1 1 1数目之和是多少,设用 v a l i val_i vali表示 N N N的最后 i i i个二进制位组成的数是多少
如果这个位置只能填 0 0 0,那么 f i = f i − 1 f_i=f_{i-1} fi=fi1
否则,当这一位填 0 0 0时,后面能产生的相邻 1 1 1的总数为 g i − 1 g_{i-1} gi1,当这一位填 1 1 1时,首先要加上 f i − 1 f_{i-1} fi1,再看看是不是第 i − 1 i-1 i1位也能填 1 1 1,如果能,再加上 v a l i − 2 + 1 val_{i-2}+1 vali2+1,即 f i = g i − 1 + f i − 1 + ( v a l i − 2 + 1 ) [ l i m i − 1 = 0 ] f_i=g_{i-1}+f_{i-1}+(val_{i-2}+1)[lim_{i-1}=0] fi=gi1+fi1+(vali2+1)[limi1=0]
令外这道题需要高精度

代码

//数位dp
#include <bits/stdc++.h>
#define maxn 100
#define maxlen 100
#define ll long long
#define cl(x) memset(x,0,sizeof(x))
using namespace std;
struct bignum
{
	ll a[maxlen], len;
	void clear(){len=1;memset(a,0,sizeof(a));}
	bignum(ll x){this->clear();for(len=0;x;x/=10)a[++len]=x%10;if(!len)len=1;}
	bignum(){this->clear();}
	ll& operator[](ll index){return a[index];}
	void print(){for(ll i=len;i;i--)putchar(a[i]+48);}
}f[maxn], g[maxn], lim[maxn], val[maxn];
bool operator<(bignum a, bignum b)
{
	ll i;
	if(a.len<b.len)return true;
	if(a.len>b.len)return false;
	for(i=a.len;i;i--)
		if(a[i]<b[i])return true;
		else if(a[i]>b[i])return false;
	return false;
}
bool operator>(bignum a, bignum b){return b<a;}
bool operator==(bignum a, bignum b){return !(a>b) and !(a<b);}
bool operator!=(bignum a, bignum b){return a<b or b<a;}
bignum operator+(bignum a, bignum b)
{
	ll len=max(a.len,b.len)+1, i;
	bignum sum;
	for(i=1;i<=len;i++)sum[i]=a[i]+b[i];
	for(i=1;i<=len;i++)sum[i+1]+=sum[i]/10, sum[i]%=10;
	if(sum[len]==0 and len>1)len--;
	sum.len=len;
	return sum;
}
bignum operator+(bignum a, ll b){return a+bignum(b);}
bignum operator-(bignum a, bignum b)
{
	ll i;
	bignum c;
	for(i=1;i<=a.len;i++)
	{
		c[i]=a[i]-b[i];
		if(c[i]<0)a[i+1]--, c[i]+=10;
	}
	for(c.len=maxlen-1;c[c.len]==0 and c.len>1;c.len--);
	return c;
}
bignum operator-(bignum a, ll b){return a-bignum(b);}
bignum operator*(bignum a, bignum b)
{
	ll i, j;
	bignum c;
	for(i=1;i<=a.len;i++)for(j=1;j<=b.len;j++)c[i+j-1]+=a[i]*b[j];
	for(i=1;i<maxlen;i++)if(c[i]>10)c[i+1]+=c[i]/10, c[i]%=10;
	for(c.len=maxlen-1;c[c.len]==0 and c.len>1;c.len--);
	return c;
}
bignum operator*(bignum a, ll b){return a*bignum(b);}
ll tot, N;
void init()
{
	ll i;
	cl(lim), cl(val), tot=0;
	for(i=1;i<maxn;i++)f[i].clear(), g[i].clear();
	while(N)lim[++tot]=N&1, val[tot]=val[tot-1]+(lim[tot]*(1ll<<tot-1)), N>>=1;
}
void dp()
{
	ll i, j;
	for(i=2;i<=tot;i++)
	{
		g[i]=g[i-1]*2+(1ll<<i-2);
	}
	for(i=2;i<=tot;i++)
	{
		if(lim[i]>bignum(0))f[i]=g[i-1]+f[i-1]+(lim[i-1])*(val[i-2]+1);
		else f[i]=f[i-1];
	}
	f[tot].print(), putchar(10);
}
int main()
{
	ll kase=0;
	while(scanf("%lld",&N),N>=0)
	{
		printf("Case %lld: ",++kase);
		init();
		dp();
	}
	return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值