Codeforces 354C. Vasya and Beautiful Arrays【DP,暴力】

題目大意:

有一組數,你可以對每一個數做減法,減去的數不超過k,問最後你最大能得到的整個數組的GCD是多少

做法:

最後要求的是GCD(假設爲g),那麼也就是說,對於數組中的數a[i]來說,減去某個不大於k的值之後,就能被g整除;換句話說,a[i]%g<=k,只要滿足該條件即可.

首先,由於只能對數進行減法操作,假設m爲數組a中的最小值,ma爲最大值;那麼答案一定不會超過m,也就是說,答案的上界是m。
再觀察上面得出的公式:a[i]%g<=k,如果g<=k+1,那麼該公式是無論如何都滿足的(因爲a[i]%g,結果一定在0~g-1之間。)。也就是說,g的下界是k+1。

那麼這樣的話,是不是就說明我們可以暴力枚舉k+1~m之間的數一個個的檢查是否是正確答案呢?

答案爲YES!
我們要求最大值,那當然是從大到小枚舉,一旦找到答案,就輸出,結束。

現在大體思路確定,解決剩下的小問題:我們應該怎麼判斷一個枚舉的數是不是正確答案呢?
如果這個當前的g是正確答案,那麼就是說,a數組中的每個數減去一個不大於k的值之後,變成了g的倍數(模g爲0);也就是說,每個數都存在於[i*g,i*g+k](1<=i<=ma/g)這樣的區間之間!那麼我們只需要判斷在這樣的區間內的個數是否等於總的數的個數,如果相等,那就是正確答案,如果不等,那麼繼續枚舉下一個答案。
那我們現在想想複雜度的問題,枚舉過程中,最大循環次數應該是這樣,n/1+n/2+n/3+......+n/n=N*logN(調和級數)複雜度也在允許範圍內~

當然,當上界m小於下界k+1時,由於0~k+1的數一定是滿足條件的,而答案又不能超過m,那毫無疑問,正確答案一定爲m。

代碼:
#include <iostream>
#include <cstdio>
#define N 1000010
using namespace std;
int a[N/3];
int sum[N*2];
int n,k,m,ma,ans;
bool running(int Ans)
{
    int limit=ma/Ans;
    int ret=0;
    for(int i=1;i<=limit;i++)
        ret+=(sum[i*Ans+k]-sum[i*Ans-1]);
    return ret==n;
}
int main()
{
    scanf("%d%d",&n,&k);
    m=11111111;
    for(int i=0;i<n;i++)
    {
        scanf("%d",a+i);
        sum[a[i]]++;
        m=min(m,a[i]);
        ma=max(ma,a[i]);
    }
    for(int i=m;i<=ma+k;i++){
        sum[i]+=sum[i-1];//求前綴和,判斷某個區間內有多少個數
        //cout<<i<<": "<<sum[i]<<endl;
    }
    if(m<=k+1) {cout<<m<<endl;return 0;}
    for(ans=m;ans>=k+1;ans--)
    {
        if(running(ans))
        {
            cout<<ans<<endl;
            return 0;
        }
    }
    return 0;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章