題意:
選擇不超過
k 個
n以內的正整數撐起來,使得乘積是乙個無平方因子數(不能夠被任意乙個質數的平方整除),一共有多少種取法? (n
≤500,k
≤500)
題解:
首先,很容易想到狀壓,壓縮當前已經選過某質數的狀態。對於質數個數小於
20 可以直接過,但是問題是現在
n 以內的質數個數很多,無法壓縮。
考慮狀壓dp的本質:從前往後依次加入數,並更新之前的所有沒有與他共用質因數的狀態。顯然,對於那些質因數分解後只有小質數的數可以直接狀壓dp,現在考慮如何加入其他大的數。
設定乙個分界點n√
來區分大小質因數,乙個數隻可能包含至多乙個大於n√
的質因數,所以每個質數都會而且只會被列舉一次,現在從小(n√
)到大列舉這些質因數:
現在已經列舉到質因數
j ,開始列舉包含
j的每個數aj
,還是將aj
質因數分解,設定乙個te
mp陣列(**中可以不設定,這裡為了方便),因為前面的所有狀態都沒有包含
j ,而且狀壓dp保證了不會重複小質數,可以通過同樣的狀壓dp方式從之前的dp狀態轉移過來。
處理完所有aj
後把tem
p 陣列加入dp
陣列繼續處理j2
即可。
其實這就是個分組揹包而已。
**中先處理了所有不包含
1 的情況,最後對於小於
k的情況乘
2 (選
1和不選
1 ),對於等於
k的情況直接加(不能選
1 ),最後再加上
1即可(只選
1 ).
#include
using
namespace
std;
inline
int read()
while(isdigit(ch))
return i*f;
}const
int lim=(1
<<8)+20,maxn=5e2+50;
const
int prime[8]=;
const
int mod=1e9+7;
int t,n,k,dp[maxn][lim],status[maxn],lim=(1
<<8)-1;
vector
v[maxn];
inline
void pre()
else
if(cnt)status[i]^=(1
if(tmp==1)v[i].push_back(i);
else v[tmp].push_back(i);
}}int main()}}
int ans=0;
for(register
int i=1;i<=k;i++)
}cout
<1
<}
}
NOIP模擬 乘積 狀壓dp 多組揹包
題目大意 選擇不超過k個n以內的正整數相乘,使乘積使乙個無平方因子數,問有多少種取法?每個數只能取一次 1 k,n 500 解題思路 首先可以想到把有平方因子數的數刪了。那問題也就變成了使取得的數的質因數集合無交集。如果質因數個數足夠少,我們就可以狀壓記錄每個質數取還是沒取,但500的範圍太大。不過...
NOI P模擬賽 奶油蛋糕塔(狀壓 DP)
資料範圍1 n 5 1 051 leq n leq5 times10 5 1 n 5 105 n 20 n leq 20 n 20 的狀壓應該都會吧,狀態記錄已經選了的蛋糕集合,以及蛋糕序列的尾部奶油,然後列舉蛋糕轉移。總共有 10 1010 種不同的蛋糕,資料很小。把最後的蛋糕塔等效為乙個序列,如...
校內模擬 記憶(狀壓DP)
考場想到了正解,然後被卡快取記憶體,gg 乙個顯然的轉化就是設e ie i ei 表示朋友選擇第i ii個串的時候的期望操作次數。則答案就是所有e ie i ei 的平均值。首先考慮乙個o n l2l o nl2 l o nl2l 的暴力,對於每個串,列舉所有其他串看有多少個位置相同,則我們能夠知道...