1. 程式人生 > 實用技巧 >題解 P4759 【[CERC2014]Sums】

題解 P4759 【[CERC2014]Sums】

話不多說,我們直入正題
這是一題數論題,主要考察等差數列知識。

【part 1】題意簡述

給你T個數,請把每個數拆分成幾個連續自然數相加,無法拆分輸出IMPOSSIBLE。

【part 2】演算法&思路分析

首先,我們只考慮只有一個數的情況:
設這個數為N,N = a + (a + 1) + (a + 2) + ...+ (a + n - 1)
也就是說,起始數是a,共有n項。
接下來,我們就有兩個思路:

  • 列舉a

    這是最容易想到的思路,可以發現一個性質:a<=n/2,所以我們要列舉n/2次,但還要繼續拓展,所以時間複雜度為O(N/2*n),肯定超時

  • 列舉n

    這是本題正解。
    N = (a+a+n-1)×n/2
    因為a為正整數
    所以2a+n-1>n
    因此n<=sqrt(N×2)
    N = (a+a+n-1)*n/2
    2×N/n = 2a+n-1;
    移項可以得出a = (2×N/n-n+1)/2
    當a為正整數時有解。

【part 3】程式碼分析

#include <iostream>
#include <algorithm>
#include <cmath>

using namespace std;

int T , n;

int main() {
//  freopen(".in","r",stdin);
//  freopen(".out","w",stdout);
    cin >> T;
    while(T --) {
        cin >> n;//輸入
        bool found = 1;//判斷是否能找到
        for(int i = 2; i <= sqrt(2 * n); i ++) {//n<sqrt(2*N)
            int a = ( (2 * n) / i - i + 1) / 2;
            if( (a * 2 ) == + 1 - i + ( (2 * n) / i )) {
                if( (a * 2 - 1 + i ) * i == 2 * n) {
                    cout << n << " = ";
                    for(int j = 1; j <= i-1; j ++)
                        cout << a << " + " , a ++;
                    cout << a << endl;
                    found = 0;
                    break;
                }//判斷a是否為整數,若為整數則輸出
            }//時刻注意double精度誤差!
        }
        if(found)   cout << "IMPOSSIBLE" << endl;
    }
    return 0;//完結撒花
}