【原創題目】lym子集

【題目名稱】lym子集

【時間限制】1000ms

【空間限制】128M

【測試數據】測試數據包,密碼:4nbc

【題目描述】

給定一個含有n個元素的集合A.由於集合中元素的互異性,A中的任何兩個元素都是不相等的。如果集合A的子集S含有至少2個元素,且集合S中的任何兩個元素之差的絕對值都大於k,則稱該子集S爲集合A的lym子集。

對於給定的集合A與參數k,這樣的lym子集可能不止一個.比如:對集合A={1,2,3},當k=0時,集合A的lym子集有{1,2},{1,3},{2,3},{1,2,3},共4個。

現在已知集合A中的元素與非負整數k,你的程序需要求出集合A的lym子集的數目。由於答案可能很大,請將答案mod 19991202後輸出.

【輸入格式】

第1行,兩個整數n和k,含義見題目描述.

第2行,n個整數,表示集合A中的n個元素.

(數據保證各元素均不相同).

【輸出格式】

一個非負整數,表示集合A的lym子集的數目。由於答案可能很大,mod 19991202後輸出.

【樣例輸入1】

3 0

1 2 3

【樣例輸出1】

4

【樣例輸入2】

5 1

2 3 4 1 5

【樣例輸出2】

7

【樣例說明】

樣例1:lym子集有且僅有{1,2},{1,3},{2,3},{1,2,3}.

樣例2:lym子集有且僅有{1,3},{1,4},{1,5},{2,4},{2,5},{3,5},{1,3,5}.

 【提示】

對於20%的數據,2<=n<=20;

對於50%的數據,2<=n<=5000;

對於100%的數據,2<=n<=200000,0<=k<=10^9.

所有輸入數據均爲整數,集合元素可能爲負,但保證在int範圍內.

【聲明】

此爲lym01803原創題目。

【題解】

這道題是本人在做教輔<五·三題霸>時,看到一道數列題後有所感悟,從而改編而成。既然是由數列題改編而成,那必定與遞推式緊密相關。

首先拿到這道題,基本的想法是:要保證任何兩個數的差值大於k,其充要條件是差值最小的兩個數之差大於k.不過知道這個等價命題也沒有什麼用處,真正有用的是:我們如果把這些數從小到大排序,那麼就只需要考慮相鄰兩個數的關係。如果保證相鄰的兩個數之差都大於k,那麼最後的S集合就是滿足要求的。

首先我們將n個數升序排列,記第i數爲a[i],前i個數的lym子集數量記作f[i].

我們不妨考慮一下f[i]的構成,即狀態轉移方程(本質上並沒有決策,所以不算動規,但思路很像動規).

1.首先這些集合可以不含第i個數a[i].這一類的數量等同於f[i-1];

2.如果這些集合包含第i個數:

(1)假如a[i]-a[1]<=k,那麼我們一定找不出合題的lym子集,f[i]=0;

(2)假如a[i]-a[1]>k,那麼我們一定可以找到一個最大的p,使得a[i]-a[p]>k成立,其中1<=p<i;這樣一來,對於任意 1<=t<=p,總有a[i]-a[t]>k;且對於任意的p<t<i,總有a[i]-a[t]<=k;現在對於a[p+1]~a[i-1]之間的任何數,都 不能與a[i]同時出現,反之a[1]~a[p]之間的任何數都可以與a[i]組合在一起:

(i)於是我們把a[1]~a[p]構成的lym子集,與單元素a[i]並起來,就可以得到新的含有a[i]的lym子集。數目爲f[p].

(ii)我們也可以將a[1]~a[p]之間的單個元素與a[i]組合起來,形成新的雙元素lym子集,數目爲p.

於是f[i]=f[i-1]+f[p]+p.(前提是存在p).

至於怎麼找p,我想這麼大的數據規模,不至於一個一個挨着找吧?二分查找是正解。

時間複雜度O(n*log n),空間複雜度O(n).

【參考代碼】(這次放的是標程,因爲是自己出的題)

#include<cstdio>  
#include<algorithm>  
using namespace std;   
#define MAXN 210100  
#define Mod 19991202  
void r(int& x)  
{  
    bool f=0;  
    char c=getchar();  
    while(c<'0'||c>'9')  
    {  
        if(c=='-')  
        {  
            f=1;  
        }   
        c=getchar();  
    }  
    for(x=0;c>='0'&&c<='9';c=getchar())  
    {  
        x=(x<<1)+(x<<3)+c-'0';  
    }  
    if(f)  
    {  
        x=-x;  
    }  
    return ;  
}//因爲讀入數據很多,scanf()效率堪憂,所以採用輸入優化。  
int n,k,a[MAXN],f[MAXN];  
bool cmp(int a,int b)  
{  
    return a<b;  
}  
int find(int kk)  
{  
    int l=1,mid,r=n;  
    while(l<=r)  
    {  
        mid=(l+r)>>1;  
        if(a[mid]>kk)  
        {  
            r=mid-1;  
        }  
        if(a[mid]<kk)  
        {  
            l=mid+1;  
        }  
        if(a[mid]==kk)  
        {  
            return mid;  
        }  
    }  
    return r;  
}//二分查找;  
int main()  
{  
    r(n);  
    r(k);   
    for(int i=1;i<=n;i++)  
    {  
        r(a[i]);  
    }  
    sort(a+1,a+1+n,cmp);//排序;  
    for(int i=2;i<=n;i++)  
    {  
        if(a[i]-a[1]<=k)  
        {  
            f[i]=0;  
        }  
        else  
        {     
            int p=find(a[i]-k-1);  
            f[i]=f[i-1]+f[p]+p;  
            f[i]%=Mod;        
        }  
    }  
    printf("%d",f[n]);  
    return 0;  
}  


發佈了31 篇原創文章 · 獲贊 6 · 訪問量 1萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章