點此看題
首先有乙個樸素dpdp
dp,因為每個數字都只會最多出現1
11次,而且出現數字相同的不同情況最後也可以一起算答案(和一定),那麼我們只需要統計出方案數,dp[
i]
dp[i]
dp[i
]為二進位制位出現的裝壓為i
ii,轉移列舉包含i
ii的狀態j
jj,設a[i
]a[i]
a[i]
為值i
ii出現次數,0
00需要單獨考慮,轉移為:
d p[
j]=d
p[j⊕
i]×a
[i
]dp[j]=dp[j\oplus i]\times a[i]
dp[j]=
dp[j
⊕i]×
a[i]
這樣做o
(min(
a,n)
a)
o(\min(a,n)a)
o(min(a,
n)a)
,優化j
jj的列舉方式,找到大於等於i
ii的全1
11的數,那麼我們可以拿到i
ii在這個集合下的補集,那麼列舉這個補集的子集,就是j
jj比i
ii多出來的一部分,這樣轉移的複雜度是o(3
k)
o(3^k)
o(3k)(2
k>
a2^k>a
2k>a)
#include
#include
using
namespace std;
const
int m =
300005
;const
int jzm =
1e9+7;
intread()
int n,m,k,mx,cnt,ans,a[m]
,b[m]
,dp[m]
,phi[m]
,p[m]
;void
sieve
(int n)
for(
int j=
1;j<=cnt && i*p[j]
<=n;j++
) phi[i*p[j]
]=phi[i]
*phi[p[j]];
}}}int
main()
m=1;
while
(m<=mx)
m*=2
;sieve
(m);
m--;dp[0]
=1;for
(int i=
1;i<=mx;i++)}
for(
int i=
0;i<=m;i++
) ans=
(ans+
1ll*dp[i]
*phi[
1+i]
)%jzm;
for(
int i=
1;i<=k;i++
) ans=
2ll*ans%jzm;
printf
("%d\n"
,ans)
;}
NOI Online 3 提高組 優秀子串行
傳送 這題剛開始看到那麼多式子,確實沒啥思路。但是再仔細想一想會發現挺有意思的。因為 b 序列的限制,每乙個 b i 的二進位制中的1必定只有他自己有,那麼 sum b i 就是把他們按位與起來。我們令 dp s 表示 sum b i s 的 b 序列個數,那麼答案就是 dp i sum phi i...
NOI Online 3 提高組 水壺
在乙個長度為 n 的序列中求出長度為 k 1 的最大子段。本題唯一的需要注意的一點是當 k n 時要輸出所有數的和,像我的考場 scanf d d n,k k if k n k n for int i 1 i n i scanf d a i for int i 1 i k i op a i for ...
NOI Online 3 提高組 魔法值
我是真沒想到可以用 flo yd floyd floy d 好吧其實也不是正宗的吧 感覺這個演算法已經被我遺忘了 qwq qwqqw q。結果考場上把快速冪又打爆了 定義 to i j k to i j k to i j k 為 i ii 到 j jj 的路徑長度為 2 k2 k 2k的方案總數,d...