AT2067 たくさんの数式 / Many Formulas
题意
有一个仅由字符1到9构成的字符串S(1≤∣S∣≤10),让你在中间添加+,使其变成一个加式。求所有方案的和值(详见样例解释)。
输入125,输出176.
有4种:
125
1+25=26
12+5=17
1+2+5=8
题解
(驳回无脑暴搜)
看到题目直接想到的就是
对于字符串中的每一段数字分别计算贡献再求和
每一段数字的贡献就是 数值*安排加号的方案数(该数值两边的加号已经安排好了)
对于一个长为tot的序列 中间可以插入加号的的空为(tot-1)个
那么对于一段长为len的数字 它安排加号方案数的计算方法如下:
情况一:该段数字并没有处在边界上(没有拿第一位数字或者最后一位数字)
1.该段数字使得其内部无法填入加号,坑位减少(len-1)个;
2.该段数字两边无法填入加号,坑位减少2个;
3.合计减少坑位(len+1)个。
剩余坑位为(tot-1-len-1)=(tot-len-2)个
情况二:该段数字处在边界上
1.内部无法填入加号,坑位减少(len-1)个;
2.该段数字的一边无法填入加号(另一边是边界,本来就没得坑位),坑位减少1个;
3.合计减少坑位(len)个。
剩余坑位为(tot-1-len)=(tot-len-1)个。
那么知道坑位个数就可以算出方案数了吧
对于每个坑可以填也可以不填,所以方案数就是2^(坑数)。
最后累加就行了。
还有一个关键点就是如何枚举每一段数字,这里我的方法是先枚举长度,再枚举起点。
(时间比别的代码好像快那么几毫秒,纯粹是赢在算法上?)
代码
#include <cstdio>
#define ll long long
using namespace std;
inline ll read(){
ll x=0,f=1;char c=getchar();
while (c<'0'||c>'9') {if (c=='-') f=-1;c=getchar();}
while (c>='0'&&c<='9') {x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return x*f;
}
int a[20],q[20];
int tot;
ll ans;
signed main(){
ll b=read();
while (b){
q[++tot]=b%10;
b/=10;
}
for (int i(1);i<=tot;++i) a[i]=q[tot-i+1];//可能这一步还有更优处理。
for (int l(1);l<=tot;++l)
for (int i(1);i+l-1<=tot;++i){
ll num=0;
for (int j(i);j<=i+l-1;++j) num*=10,num+=a[j];
//printf("%lld ",num);
if (i==1||i+l-1==tot){//靠在边界上
for (int i(1);i<=tot-l-1;++i) num*=2;
ans+=num;
}
else {//没靠在边界上
for (int i(1);i<=tot-l-2;++i) num*=2;
ans+=num;
}
}
printf("%lld",ans);
return 0;
}