【題目名稱】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;
}