洛谷3773 BZOJ4903 CTSC2017 吉夫特 數學 dp
阿新 • • 發佈:2018-12-13
題目連結 題意: 給你一個單調不升的陣列a,求有多少個長度大於等於2的子序列滿足。答案對1000000007取模。
題解: 首先題意是讓你求一個相鄰兩數組合數膜2意義下都是1的子序列,也就是要求相鄰兩數組合數是奇數。我們考慮對於一個組合數,如果它是個奇數,意味著我們能從中提取的因子2的個數和從中提取的因子2的個數相同。 根據組合數取模的盧卡斯定理,我們有。我們可以看出,這個式子相當於在對n和m進行二進位制分解,從二進位制的最低位到最高位依次判斷n和m在該位對應的01,我們發現,如果在某一位n=0,m=1,那麼是不存在方案的,看作結果是0,不管其他位結果如何,答案在乘0之後肯定會變成0,所以我們可以推出,如果想要,那麼需要滿足的條件就是二進位制下n的每一個是1的位,m對應位可以是1也可以是0,n是0的位,m對應位也一定要是0,也就是。如果像狀壓dp那樣把一個二進位制下的01串看作一個集合,那麼就相當於要求m在二進位制下是n的一個子集。 那麼我們考慮dp,設dp[i]為數字i結尾的方案數,那麼這個狀態可以轉移到所有i的二進位制數的子集。最後對於每一個在中出現的數,它的初值設為1,最後對答案的貢獻要減1,相當於減去了長度為1只有本身的子序列。 程式碼很短:
#include <bits/stdc++.h>
using namespace std;
int n,tong[300010],a[300010];
int dp[300010],ans;
const int mod=1000000007;
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;++i)
{
scanf("%d",&a[i]);
tong[a[i]]=i;
dp[a[i]]=1;
}
for(int i=1;i<=n;++i)
{
for(int j=(a[i]-1)&a[i];j;j=(j-1)&a[i])
{
if(tong[j]>i)
dp[j]=(dp[j]+dp[a[i]])%mod;
}
}
for(int i=1;i<=n;++i)
ans=(ans+dp[a[i]]-1)%mod;
printf("%d\n",ans);
return 0;
}