链接
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
2i−1−1,只有当这一位和前一位都填
0
0
0的时候,这一位才会对答案有贡献,所以
g
i
=
2
g
i
−
1
+
2
i
−
2
g_i=2g_{i-1}+2^{i-2}
gi=2gi−1+2i−2
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=fi−1
否则,当这一位填
0
0
0时,后面能产生的相邻
1
1
1的总数为
g
i
−
1
g_{i-1}
gi−1,当这一位填
1
1
1时,首先要加上
f
i
−
1
f_{i-1}
fi−1,再看看是不是第
i
−
1
i-1
i−1位也能填
1
1
1,如果能,再加上
v
a
l
i
−
2
+
1
val_{i-2}+1
vali−2+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=gi−1+fi−1+(vali−2+1)[limi−1=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;
}