题目链接:

codeforces 225B


题目大意:

定义f(k,n)为类似菲波那契数推导,只不过变为前k项的和,然后给出一个数s,利用k-菲波那契数构造出一个不重复的集合的元素和为s,集合的规模大于1


题目分析:

  • 首先因为菲波那契数的增长速度快的吓人,所以给的数据范围109很快就能达到,我们得到O(n)的构造出所有的可能用到的k-菲波那契数是可行的。
  • 然后就是构造如何构造的问题了。
  • 因为题目保证解一定存在,那么我们考虑如何构造一组可行解,采取的是贪心的方法,先选取能够选择的元素中最大的。
  • 如果答案的集合只有1个元素,那么我们添上0保证得到的解的规模

codeforces 225B B. Well-known Numbers(数论+二分+贪心+构造)_贪心


  • 这样如果对于给定数s的一个因数是数列中的数位置为n,倍数等于2,那么我们通过选取f(k,n+1)能够保证有合法解避免这种同一个数出现多次的冲突,找到最大的那个数,因为那个数不可能出现当前的s是它的两倍,因为s

AC代码:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <set>
#include <vector>
#define MAX 100007

using namespace std;

typedef long long LL;

LL a[MAX];
int s,k,n;
multiset<int,greater<int> > col;
vector<int> ans;

int main ( )
{
    while (~scanf ( "%d%d" , &s , &k ) )
    {
        col.clear();
        ans.clear();
        memset ( a , 0 , sizeof ( a ) );
        n = 1;
        a[1] = 1;
        a[0] = 0;
        while ( a[n]-a[n-1] <= s )
        {
            int l = max ( n-k , 0 );
            a[n+1] = a[n]-a[l];
            col.insert ( a[n+1] );
            a[n+1] += a[n];
            n++;
        }
        multiset<int>::iterator it;
        /*cout << "------------------------------" << endl;
        //multiset<int>::iterator it;
        for ( it = col.begin() ; it != col.end() ; it++ )
            cout << *it << endl;
        cout << "---------------------------" << endl;*/
        while ( s )
        {
            it = col.lower_bound ( s );
            s -= *it;
            ans.push_back ( *it );
            col.erase ( it );
        }
        while ( k!= 1 && ans.size() < 2 ) ans.push_back ( 0 );
        printf ( "%d\n" , (int)ans.size() );
        for ( int i = 0 ; i < ans.size() ; i++ )
            printf ( "%d " , ans[i] );
        puts ("");
    }
}