Quite recently a creative student Lesha had a lecture on trees. After the lecture Lesha was inspired and came up with the tree of his own which he called a k-tree.
A k-tree is an infinite rooted tree where:
- each vertex has exactly k children;
- each edge has some weight;
- if we look at the edges that goes from some vertex to its children (exactly k edges), then their weights will equal 1, 2, 3, ..., k.
The picture below shows a part of a 3-tree.
Help Dima find an answer to his question. As the number of ways can be rather large, print it modulo 1000000007 (109 + 7).
A single line contains three space-separated integers: n, k and d (1 ≤ n, k ≤ 100; 1 ≤ d ≤ k).
Print a single integer — the answer to the problem modulo 1000000007 (109 + 7).
3 3 2
3
3 3 3
1
4 3 2
6
4 5 2
7
#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int maxn=110;
const int MOD=1000000007;
int dp[2][maxn];
int n,k,d;
void solve()
{
memset(dp,0,sizeof(dp));
dp[0][0]=1;
for(int i=1;i<=n;i++)
{
for(int j=1;j<d;j++)
if(i-j>=0)
{
dp[0][i]+=dp[0][i-j];
if(dp[0][i]>=MOD)dp[0][i]-=MOD;
}
for(int j=1;j<=k;j++)
{
if(i-j<0)continue;
dp[1][i]+=dp[1][i-j];//dp[1][i]表示其中包含有>=d的,只要有一个d,那么只要从1~k中任选一个就行
if(dp[1][i]>=MOD)dp[1][i]-=MOD;
}
for(int j=d;j<=k;j++)
if(i-j>=0)
{
dp[1][i]+=dp[0][i-j];//也可以是从不包含d的加上一个>=d的形成所要求的
if(dp[1][i]>=MOD)dp[1][i]-=MOD;
}
}
printf("%d\n",dp[1][n]);
}
int main()
{
scanf("%d%d%d",&n,&k,&d);
solve();
return 0;
}
One day, after a difficult lecture a diligent student Sasha saw a graffitied desk in the classroom. She came closer and read: "Find such positive integer n, that among numbers n + 1, n + 2, ..., 2·n there are exactly m numbers which binary representation contains exactly k digits one".
The girl got interested in the task and she asked you to help her solve it. Sasha knows that you are afraid of large numbers, so she guaranteed that there is an answer that doesn't exceed 1018.
The first line contains two space-separated integers, m and k (0 ≤ m ≤ 1018; 1 ≤ k ≤ 64).
Print the required number n (1 ≤ n ≤ 1018). If there are multiple answers, print any of them.
1 1
1
3 2
5
D:题意:求一个n,使得n+1到2n这些数的二进制中恰好有k个1的数有m个。
分析:
1. 这题竟然具有二分的性质(可以打表观察), 对于同一个k, 当n1 < n2时, n1~2n1中满足条件的数字个数不会比n2~2n2多。
2. 有了二分的特性之后,问题转化为:给出x,如何求[x, x*2]之间满足条件的个数, 假设:cal(x)表示[0,x]满足条件的个数,那么就是求cal(x*2) - cal(x)。
3. 如何求cal(x)? 举个例子:假设X的二进制形式为:101010, 因为是求[0,x]中满足条件的情况,也就是 不能比x大, 那么我们可以这样考虑, 从高位往地位遍历, 假设走到i位,我们如何构造比x小的数? 可以保持i前面的数字不动, 如果此时该位为1的话, 我们可以将他变成0, 如果是0,那么我不能改变, 否则构成的数肯定比x大,这样我们就可以构成一个不超过x的数字。 然后假设前x位出现了num个1, 因为要有k个1,所以剩下的位数里面要有k-num个1,这就是组合数学了。 详细见代码。
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int maxn=70;
typedef long long LL;
LL c[maxn][maxn];
LL m,n;
int k;
void init()
{
memset(c,0,sizeof(c));
for(int i=0;i<=62;i++)c[i][0]=1;
for(int i=1;i<=62;i++)
for(int j=1;j<=i;j++)
c[i][j]=c[i-1][j]+c[i-1][j-1];
}
LL cal(LL x)
{
int cnt=0;
LL sum=0;
for(LL i=60;i>=0;i--)
{
if(x&(1LL<<i))
{
if(k-cnt>=0)
sum+=c[i][k-cnt];
cnt++;
}
}
return sum;
}
void solve()
{
LL l=1,r=(LL)1e18,mid;
while(l<=r)
{
mid=(r+l)>>1;
LL tmp=cal(mid*2)-cal(mid);
if(tmp==m){n=mid;break;}
else if(tmp>m)r=mid-1;
else l=mid+1;
//cout<<"*";
}
cout<<n<<endl;
}
int main()
{
//freopen("in.txt","r",stdin);
init();
cin>>m>>k;
solve();
return 0;
}