JZOJ 3362. 【NOI2013模拟】数数
题目
Description
神犇最近闲来无事,于是就思考哲学,研究数字之美。在神犇看来,如果一个数的各位能够被分成两个集合,而且这两个集合里的数的和相等,那么这个数就是优美的(具体原因就只有神犇才知道了)。现在神犇在思考另一个问题,在区间[A,B]中有多少个数是优美的?这个问题对于神犇来说很简单,相信对于你来说也不难。
Input
输入只有一行,包含两个整数A和B。
Output
输出只有一行,包含一个整数,代表区间[A,B]中优美的数的个数。
Sample Input
样例1:
1 11
样例2:
6354 234363
Sample Output
样例1:
1
样例2:
82340
Data Constraint
对于30%的数据,
1
≤
A
≤
B
≤
1000
1≤A≤B≤1000
1≤A≤B≤1000.
对于50%的数据,
1
≤
A
≤
B
≤
1
0
7
1≤A≤B≤10^7
1≤A≤B≤107.
对于100%的数据,
1
≤
A
≤
B
≤
1
0
9
1≤A≤B≤10^9
1≤A≤B≤109.
题解
- 题外话:
- 这题可以打表,用
不长的时间先暴搜出每 1 0 6 10^6 106的答案的前缀和(分块打表), - 询问时只用暴力计算两头不完整的块的答案,可过!!!
- 以下进入正题——
- 这题需要很多铺垫,
- 首先想想怎么判断每个数是否为优美的?
- 方法一:DFS枚举每一位放集合 1 1 1还是集合 2 2 2,然后判断两集合元素之和是否相等;
- 方法二:设 s u m sum sum为每一位的数之和,若其为奇数,自然无解,否则用背包( 0 / 1 0/1 0/1)看看每一位的数是否可以组合成 s u m 2 frac{sum}{2} 2sum;
- 方法三:直接用一个变量 p p p( l o n g l o n g long long longlong)在二进制下代替背包(自己手推一下),每加入一位 x x x,直接 p = p ∣ ( p < < x ) p=p|(p<<x) p=p∣(p<<x)。
- 尽管这些方法一个比一个快,但终究是对于每一个数单独判断的,既然范围最大达到了 1 0 9 10^9 109,那时间还是会超限,
- 所以自然想到不能单独判断每个数。
- 按照一般的思路,我们可以设 A n s [ i ] Ans[i] Ans[i]为 [ 1 , i ] [1,i] [1,i]的优美的数的个数,那么答案为 A n s [ B ] − A n s [ A − 1 ] Ans[B]-Ans[A-1] Ans[B]−Ans[A−1],
- 现在的问题变成了怎么求 A n s [ x ] Ans[x] Ans[x]?
- 设 x x x的位数为 L L L,暴力DFS枚 0 − 9 0-9 0−9各选择几个(时间极小),满足个数之和为 L L L,
- 接着按照上面最快的方法三判断这些数是否可行,不可行直接舍去,
- 可行的话则计算这些数有多少排列方式,
- 这绝对不是一条式子就能完成的事,因为需要保证排列后的数不超过上限 x x x,
- 设DFS出的 0 − 9 0-9 0−9所选的个数为 a [ i ] ( i ∈ [ 0 , 9 ] ∩ i ∈ N ) a[i](iin[0,9]∩iin N) a[i](i∈[0,9]∩i∈N)
- 再来一个DFS从高位往低位填数,
- 每到一位先来统计答案,
- 强制限定这一位所填小于 x x x的这一位,设填数为 p p p,需保证还有 p p p可选,也就是当前 a [ p ] > 0 a[p]>0 a[p]>0(显然),
- 先给 a [ p ] − 1 a[p]-1 a[p]−1,
- 接着 A n s = A n s + ( ∑ a [ i ] ) ! Π a [ i ] ! Ans=Ans+frac{(sum a[i])!}{Pi a[i]!} Ans=Ans+Πa[i]!(∑a[i])!(因为当前位已经小于了,所以剩下的数可以任意顺序填,同时除去相同的数顺序调换的方案),
- 再给 a [ p ] + 1 a[p]+1 a[p]+1;
- 接着再来向后填数,
- 这是强制限定这一位所填等于 x x x的这一位,
- 保证 a [ p ] > 0 a[p]>0 a[p]>0,给 a [ p ] − 1 a[p]-1 a[p]−1,DFS进去后一位,回溯时 a [ p ] + 1 a[p]+1 a[p]+1。
- 这样就能保证每种填数的情况是不重不漏的了,
- 但是,我们会发现,这样填下去在统计答案时(见上方)怎样填出的数总是小于原数 x x x,
- 所以最终的答案应该为 A n s [ B + 1 ] − A n s [ A ] Ans[B+1]-Ans[A] Ans[B+1]−Ans[A].
代码
#include<cstdio>
#include<cstring>
using namespace std;
#define LL long long
int c[11],a[10],ans,ls,f[10];
void dfs1(int k)
{
if(k>ls) return;
for(int i=0;i<c[ls-k+1];i++) if(a[i])
{
a[i]--;
int s=0;
for(int j=0;j<=9;j++) s+=a[j];
s=f[s];
for(int j=0;j<=9;j++) s/=f[a[j]];
ans+=s;
a[i]++;
}
int i=c[ls-k+1];
if(a[i]) a[i]--,dfs1(k+1),a[i]++;
}
void dfs(int k,int s,int sum,int x)
{
if(k>9)
{
if(sum%2) return;
if(a[0]==ls) return ;
LL p=1;
for(int i=0;i<=9;i++)
{
for(int j=1;j<=a[i];j++)
{
p=(p<<i)|p;
p%=(LL)1<<(LL)50;
}
}
LL r=(LL)1<<(LL)(sum/2);
if((p&r)==0) return;
dfs1(1);
return;
}
if(k==9)
{
a[k]=ls-s;
dfs(k+1,ls,sum+9*(ls-s),x);
return;
}
for(int i=0;i<=ls-s;i++)
{
a[k]=i;
dfs(k+1,s+i,sum+k*i,x);
}
}
int count(int x)
{
if(x==0) return 0;
int t=x;ls=0;
while(t) c[++ls]=t%10,t/=10;
ans=0;
memset(a,0,sizeof(a));
dfs(0,0,0,x);
return ans;
}
int main()
{
int a,b;
scanf("%d%d",&a,&b);
f[0]=1;
for(int i=1;i<=9;i++) f[i]=f[i-1]*i;
printf("%d",count(b+1)-count(a));
return 0;
}