1. 程式人生 > >[NOIP模擬][狀壓DP]乘積

[NOIP模擬][狀壓DP]乘積

題目描述:
題目大意:
給出n和k,求從小於等於n的數中取出不超過k個,其乘積是無平方因子數的方案數。無平方因子數:不能被質數的平方整除。
樣例輸入:

3
1 1
6 4
4 2

樣例輸出:

1
19
6

題目大意:
狀壓dp+分組揹包
1~n中每個數含有的大於n的質因數最多有1個,而n=500內的質數只有8個:2,3,5,7,11,13,17,19。考慮這樣將1~n的數分組,含有大於19的質因子數分為一組(如 23,46,69一組,如29,58一組),其餘的單獨成組。組內的每個元素用8位二進位制表示該數含有的小於等於19的質因子。這樣分組是因為小於等於n的質數只有8個可以方便的進行狀壓dp,而含有大於19的質因子的數難以在有限空間內狀壓,於是將其放在一個組內,dp時就只取出該組中的一個數,避免了乘積大於19的質因子。
因為程式碼用了vector,vector呼叫比較慢,出題人又專門卡常,所以需要一些優秀的卡常技巧。
附程式碼:

#include<iostream>
#include<cstring>
#include<string>
#include<cstdlib>
#include<cstdio>
#include<ctime>
#include<cmath>
#include<cctype>
#include<iomanip>
#include<algorithm>
#include<queue>
#include<vector>
using namespace
std; const int N=600; const int M=(1<<8)-1; const int mod=1e9+7; const int prime[8]={2,3,5,7,11,13,17,19}; int f[N][M+10],num[N],exist[N],n,k,t,temp[N],ans; vector<int> g[N]; int readint() { char ch;int i=0,f=1; for(ch=getchar();(ch<'0'||ch>'9')&&ch!='-';ch=getchar()); if
(ch=='-') {ch=getchar();f=-1;} for(;ch>='0'&&ch<='9';ch=getchar()) i=(i<<3)+(i<<1)+ch-'0'; return i*f; } void prework() { for(int i=1;i<=n;i++) { int now=i;exist[i]=true; for(int j=0;j<8;j++) if(now%(prime[j]*prime[j])==0)//判斷此數是否符合是無平方因子數 { exist[i]=false; break; } else if(now%prime[j]==0)//表示這個數含有小於等於19的質因子 now/=prime[j],num[i]|=(1<<j);//記錄狀態,包含哪些小於等於19的質因子 if(exist[i]) { if(now==1) g[i].push_back(i);//不含大於19的質因子,單獨成組 else g[now].push_back(i); //前面每次都在除,還有剩餘,說明含大於19的質因子,於是分到質因子那組 } } } void add(int &x,int t)//為了卡常 { x+=t; if(x>=mod) x-=mod; } int main() { //freopen("mul.in","r",stdin); //freopen("mul.out","w",stdout); t=readint(); while(t--) { n=readint();k=readint(); memset(f,0,sizeof(f)); for(int i=1;i<=n;i++) g[i].clear(); prework(); f[0][0]=1; for(int i=1;i<=n;i++)//其實是在列舉每一個分組 if(exist[i]&&g[i].size()!=0)//此數合法且此組不為空 { for(int j=g[i].size()-1;j>=0;j--)//為了卡常 temp[j]=num[g[i][j]]; for(int l=k-1;l>=0;l--)//倒序,因為下面的更新,是把上一層的更新到現在的i,正序會讓這一層覆蓋掉上一層的結果 for(int j=g[i].size()-1;j>=0;j--)//列舉這一組 for(int s=(M^temp[j]),t=s;;s=((s-1)&t))//s表示狀態,第一節是指只不選temp[j],第三節是在跳s的每個子集 { add(f[l+1][s|temp[j]],f[l][s]);//f[l][s]存的是指上一層計算出來的結果 if(s==0) break;//子集為空break,之所以不放在for迴圈第二節,是因為我們需要更新一次子集為空的轉移 } } ans=0; for(int i=1;i<=k;i++)//統計各種情況和 for(int j=0;j<=M;j++) add(ans,f[i][j]); cout<<ans<<'\n';//printf("%d\n",ans); } return 0; }

相關推薦

[NOIP模擬][DP]乘積

題目描述: 題目大意: 給出n和k,求從小於等於n的數中取出不超過k個,其乘積是無平方因子數的方案數。無平方因子數:不能被質數的平方整除。 樣例輸入: 3 1 1 6 4 4 2 樣例輸出: 1 19

不死的LYM NOIP模擬 二分+DP

gis using getchar() == cstring int algo true check 一共也就7種課,第7種可以貪心地選擇一定睡覺以換取答案的最小值。 那麽我們就只剩下六種課需要討論,狀態壓縮一下【當前的課之前睡過哪些課】即可。 本題要在二分的check

2018.10.01 NOIP模擬 偷書(dp

傳送門 狀壓dp經典題。 令f[i][j]f[i][j]f[i][j]表示到第i個,第i−k+1i-k+1i−k+1~iii個物品的狀態是j時的最大總和。 然後簡單維護一下轉移就行了。 由於想皮一下果斷

2018.10.05 NOIP模擬 上升序列(dp

描述 給出一個長度為 m 的上升序列 A(1 ≤ A[i]≤ n), 請你求出有多少種 1…n 的排列, 滿足 A 是它的一個 LIS. 輸入 第一行兩個整數 n,m. 接下來一行 m 個整數, 表示

2018.10.17 NOIP模擬 管道(dp

傳送門 狀壓dp好題。 怎麼今天道道題都有點東西啊 對於今天題目神仙出題人先膜為上策:%%%%DzYoAk_UoI%%%% 設f[i][j]f[i][j]f[i][j]表示選取點的狀態集合為iii,當

11-1 noip模擬 第二題 SPFA+dp

#include #include #include #include using namespace std; struct edge{ long long y,l,z; }f[50010*2]; long long i,j,g[10010],n,m,k,t,dis[17][10010],T,d[

【題解】[牛客網NOIP賽前集訓營-提高組(第二場)]C.集合劃分 DP

題目連結 看了題解後還是沒寫對,只能去看Komachi大佬咋寫的了。 #include<cstdio> #include<cstring> const int N=18,MX=(1<<18)+5; int n,m,k,ban[N]

dp NOIP 2016 憤怒的小鳥

題意:有 n n n只豬,第

2018.10.01【校內模擬】偷書(DP

描述 在L的書架上,有N本精彩絕倫的書籍,每本書價值不菲。 M是一個書籍愛好者,他對L的書籍早就垂涎三尺。最後他忍受不了誘惑,覺得去偷L的書,為了迅速完成這件事,同時他不希望L很快發現書籍少了,他決定偷書時,對於任意連續的k本書,他最多選B本,最少選A本。現在他

jzoj5990. 【北大2019冬令營模擬2019.1.6】Bear (dp

題面 題解 我永遠討厭dp.jpg 搞了一個下午優化複雜度最後發現只要有一個小trick就可以A了→_→。全場都插頭dp就我一個狀壓跑得賊慢…… 不難發現我們可以狀壓,對於每一行,用狀態\(S\)表示有哪些格子是已經被上一行推倒了的,那麼我們可以列舉本行所有格子的字母情況,然後計算一下這個時候下

DP】【NOIP提高組】憤怒的小鳥

這是道不算水的狀壓DP 這道題對我的吸引力很大,為什麼呢,因為它的背景是遊戲啊 題目描述 Kiana 最近沉迷於一款神奇的遊戲無法自拔。 簡單來說,這款遊戲是在一個平面上進行的。 有一架彈弓位於 (0,0)(0,0) 處,每次 Kiana 可以用它向第一象限發射

[模擬考試題][神題]Star Sky[DP][BFS][差分]

題意: 給你n個排成一排的燈,0代表開著,1代表關著,有k盞開著的燈。現在你有m種不同長度的電線,可以使得leni那麼長的區間反轉(0變1,1變0)。現在問你使得整個區間的燈全部開著的最少的運算元。 N <= 40000 K <= 8 M <= 64 真心

NOIP專題複習(三) DP學習筆記

其實並不是三,已經走了很多專題了。 之後慢慢填坑吧。 我覺得學普通的DP已經救不了我了。 發覺似乎NOIP狀壓蠻裸的(flag立的飛起),於是學一波。 其實在下作為一隻蒟蒻,認為狀壓DP屬於很好理解但不太好寫的型別。 還是從經典例題互不侵犯開始。

BJ模擬 裝飾地板【dp+特徵多項式優化矩陣快速冪】

題目大意: 給一個m∗nm∗n的地板,有s1s1種1×21×2的橫地磚,s2s2種2×12×1的豎地磚,問有多少種鋪滿的方式,對998244353取模。 (m≤6,n≤102501,s1,s2≤1e9)(m≤6,n≤102501,s1,s2≤1e9)

noip 2016(憤怒的小鳥)(dp

狀壓dp,每位代表一隻豬,1為豬打到了,0為豬還沒打到 兩個豬的座標確定一條拋弧線(即為打出鳥的軌跡) g[i][j]表示由i豬和j豬確定的拋弧線,能打到的豬。(是一個二進位制的狀態) 預處理:把能被i豬和j豬確定的這條拋弧線能打到的豬都用狀壓的1來表示,存到g[i][

DP】poj3254 Corn Fields

一行 cstring fields while state 條件 style 狀壓 () 題意: 一塊n*m的田,1表示這個地方可以種植,0代表這個地方不能種植。植物種植還必須滿足兩株植物不能相鄰(橫豎都不行)。問共有幾種種植方法,而且當什麽都不種時認為是一種方法。 解題思

【bzoj4145】[AMPPZ2014]The Prices dp

return std sin highlight string span 題目 狀態壓縮dp print 原文地址:http://www.cnblogs.com/GXZlegend/p/6832200.html 題目描述 你要購買m種物品各一件,一共有n家商店,你到第i家

DP入門——鋪磚塊

ont 輸出 fin load www ret mil times set 題目描述 現有n*m的一塊地板,需要用1*2的磚塊去鋪滿,中間不能留有空隙。問這樣方案有多少種 輸入 輸入n,m(1<=n, m<=11) 有多組輸入數據,以m=n=0結束

[LightOJ 1018]Brush (IV)[DP]

邊界 lightoj 計算 const 擁有 for ostream 當前 blank 題目鏈接:http://lightoj.com/volume_showproblem.php?problem=1018 題意分析:平面上有不超過N個點,如今能夠隨意方向劃直線將它們劃

SGU 223 little kings BSOJ2772 DP

而且 進制 print 剪枝 描述 計算機 tex 範圍 blog 1896 [SCOI2005]互不侵犯King 【問題描述】在n*n(1<=n<=10)的棋盤上放k(0<=k<=n*n)個國王(可攻擊相鄰的8 個格子),求使它們無法互相攻擊的方