hdu5171 GTY's birthday gift(BestCoder Round #29 1002)

来源:互联网 发布:校园网络建设招标书 编辑:程序博客网 时间:2024/05/23 01:19

题意: 递推数列求和

  给一个有n个数的可重集S,然后进行k次下述操作:每次找出其中最大的两个数ab,把a+b这个元素加入S.问最后可重集S的元素和,结果对10000007取余.
  2n100000,1k1000000000

符号定义

符号 说明 a,b 假设ab {di}
di=aa+bdi1+di2(i=0)(i=1)(i>1)
Si
Si={Si1+di(i=0)(i>0)

方法一: 利用周期性

  因为是对MOD=10000007取余,所以di肯定存在一个值不大于MOD的周期c,即有di+c=di.(请思考并理解这段话后再看后文)
  

对应代码中的第44~52行
  c的算法:利用di的递推式计算,当出现di=adi1=b时,此时的i就是c的值.
  用s1存储一段周期的元素和.
  
  这段代码结束后,有两种情况:①循环正常结束,表示k比较小,还不用算出c,已经得到答案了.②循环被break,表示此时的i即为c的值.
  两种情况可以用第51~59行的代码统一处理.

  第5~14行是读入外挂,第27~37行是在数组中找出最大的两个数的一种方法.

 #include <algorithm> #include <iostream>using namespace std; #define ISDIG ((c=getchar())>='0'&&c<='9')template <typename T>inline int read(T& x) {    int c, sign = 1; x = 0;    while (!(ISDIG || c == '-')) if (c == EOF) return c;    if (c == '-') sign = -1; else x = x*10 + c - '0';    while (ISDIG) x = x*10 + c - '0';    x *= sign;    return 1;}const int MOD = 10000007;int d[MOD + 10];int main() {    int n, k, a, b;    int t, i; // 临时变量、计数器    while (cin >> n >> k) {        read(a); read(b);        int s = a + b;        for (i = 2; i < n; i++) {            //{找出初始S中最大的两个数a,b            read(t);            if (t > a) {                if (t > b) {                    if (a < b) a = t;                    else b = t;                }                else a = t;            }            else if (t > b) b = t;            //}            s = (s + t) % MOD;        }        if (b > a) swap(a , b); // 假设a>=b        d[0] = a;        d[1] = a + b;        //{计算出周期c和这段周期的元素和s1        int c, s1 = d[1];        for (i = 2; i <= k; i++) {            d[i] = (d[i-1] + d[i-2]) % MOD;            s1 = (d[i] + s1) % MOD;            if (d[i] == a && d[i-1] == b) break;        }        c = i;        //}        do {            s = (s + s1) % MOD;            k -= c;        } while (k > c);        // 去掉完整的周期后,还有些残余的末尾项要加上        for (i = 1; i <= k; i++) s = (d[i] + s) % MOD;        cout << s << "\n";    }    return 0;}

  注意也只有64MB的内存限制才可以用这个方法,因为32MB只能开辟800万大小的一个int数组╮(╯▽╰)╭.

方法二: 矩阵快速幂模

  矩阵快速幂模的解法原理,我就不讲这么详细了.去年做过这道比较基础的矩阵快速幂模题:FOJ 1683 纪念SlingShot,那道题理解后,此类题型的基本思想就掌握了,这题难度差不多.
   FOJ 1692 Key problem这篇博客讲了道更难的矩阵快速幂模题.

  这题关键是推出如下的公式:

100111110kS0ab=Skdkdk1

  借着做这题的机会,开发个新的矩阵类.

 #include <algorithm> #include <cstring> #include <iostream>using namespace std; #define ISDIG ((c=getchar())>='0'&&c<='9')template <typename T>inline int read(T& x) {    int c, sign = 1; x = 0;    while (!(ISDIG || c == '-')) if (c == EOF) return c;    if (c == '-') sign = -1; else x = x*10 + c - '0';    while (ISDIG) x = x*10 + c - '0';    x *= sign;    return 1;}/*矩阵类(用于快速幂模)  1、T是整型类型,N是方阵大小,MOD是取余的值  2、Eye是单位阵*/template <typename T, const int N, const int MOD>class Matrix {    T val[N][N];public:    Matrix() { memset(val, 0, sizeof(val)); }    Matrix(T a[N][N]) { memcpy(val, a, sizeof(val)); }    Matrix operator*(const Matrix& c) const {        Matrix res;        for (int i = 0; i < N; ++i)            for (int j = 0; j < N; ++j)                for (int k = 0; k < N; ++k) {                    res.val[i][j] += val[i][k] * c.val[k][j];                    //防止矩阵元素变为负数,若不需要,去掉"+MOD"                    res.val[i][j] = (res.val[i][j] + MOD) % MOD;                }        return res;    }    Matrix& operator*=(const Matrix& c) {        *this = *this * c;        return *this;    }    Matrix operator^(int k) const { //返回*this^k        Matrix res = Eye();        Matrix step(*this);        while (k) {            if (k & 1) res *= step;            k >>= 1;            step *= step;        }        return res;    }    Matrix Eye() const {        Matrix a;        for (int i = 0; i < N; i++) a.val[i][i] = 1;        return a;    }    void out() const {        for (int i = 0; i < N; i++) {            for (int j = 0; j < N; j++)                cout << val[i][j] << " ";            cout << "\n";        }    }    T* operator[](int i) { return val[i]; }};typedef long long LL;const int MOD = 10000007;int main() {    int n, k, a, b, t;    while (cin >> n >> k) {        read(a); read(b);        int s = a + b;        for (int i = 2; i < n; i++) {            //{找出初始S中最大的两个数a,b            read(t);            if (t > a) {                if (t > b) {                    if (a < b) a =t;                    else b = t;                }                else a = t;            }            else if (t > b) b = t;            //}            s = (s + t) % MOD;        }        if (b > a) swap(a, b); // 假设a>=b        LL aa[3][3]={{1,1,1},{0,1,1},{0,1,0}};        Matrix<LL,3,MOD> A(aa), X, Y;        X[0][0] = s, X[1][0] = a, X[2][0] = b;        Y = (A^k) * X;        cout << Y[0][0] << "\n";    }    return 0;}
1 0
原创粉丝点击