Codeforces Round #247 (Div. 2)

C. k-Tree

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.

As soon as Dima, a good friend of Lesha, found out about the tree, he immediately wondered: "How many paths of total weight n (the sum of all weights of the edges in the path) are there, starting from the root of a k-tree and also containing at least one edge of weight at least d?".

Help Dima find an answer to his question. As the number of ways can be rather large, print it modulo 1000000007 (109 + 7).

Input

A single line contains three space-separated integers: n, k and d (1 ≤ n, k ≤ 100; 1 ≤ d ≤ k).

Output

Print a single integer — the answer to the problem modulo 1000000007 (109 + 7).

Sample test(s)
Input
3 3 2
Output
3
Input
3 3 3
Output
1
Input
4 3 2
Output
6
Input
4 5 2
Output
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;
}

D. Random Task

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, ..., 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.

Input

The first line contains two space-separated integers, m and k (0 ≤ m ≤ 1018; 1 ≤ k ≤ 64).

Output

Print the required number n (1 ≤ n ≤ 1018). If there are multiple answers, print any of them.

Sample test(s)
Input
1 1
Output
1
Input
3 2
Output
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;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值