鏈結
能學好多東西
題意描述:
兩個人各從正整數 2 ~ n(<=500) 中取一些數,可以不取;若取出的兩個集合中,任意屬於不同集合的的兩個元素都互質,則方案合法;求合法方案數首先注意到乙個方案合法的等價條件是:兩個人的質數集合沒有交集
可以想到 \(f[s1][s2]\) 表示兩個人的質數集合為 s1 和 s2 \((s1\and s2=0)\) 時的方案數,當然還有隱藏的一維:前 i 個數
設第 i 個數的質數集合為 s,則有遞推式:
\[f[i][s1|s][s2] += f[i-1][s1][s2], \ (s\and s2=0) \\ f[i][s1][s2|s] += f[i-1][s1][s2], \ (s\and s1=0)
\]但是 n=500 時顯然狀壓不下那麼多的質數
我們需要注意到乙個性質:對於乙個數 \(x\),最多只有乙個大於 \(\sqrt x\) 的質因子
對應此題,即所有數最多只有乙個大於 22 的質因子,而小於 22 的質數只有 8 個
我們可以考慮只狀壓前 8 個質數,對於更大的質數的不重複取,用排序來處理:那個大質數,要麼屬於集合 s1,要麼 s2,要麼都不屬於
對每個數字,預處理得到其質因子的前 8 個質數的集合 s,和唯一乙個大質數 p(無則設為 1)
所有數字按 p 排序,然後遞推:
設 \(f[i][s1][s2]\) 表示前 i 個數,(前 8 個)質數集合為 s1, s2 的方案數,設 \(f1[i][s1][s2]\) 表示當前的大質數只可能由第 1 個人取的方案數,\(f2[i][s1][s2]\) 同理
則對於當前出現的乙個新的大質數(對應的一段區間),首先將 f 複製給 f1, f2,然後 f1 和 f2 按各自的規則遞推,最後再相加賦值給 f;兩者都不包括當前質數的情況算了兩次,所以要扣除一倍 f:\(f[s1][s2] = f1[s1][s2] + f2[s1][s2] - f[s1][s2]\)
\[f1[i][s1|s][s2] += f1[i-1][s1][s2], \ (s\and s2=0) \\ f2[i][s1][s2|s] += f2[i-1][s1][s2], \ (s\and s1=0)
\]實現的時候我們可以不用滾動陣列,注意到被貢獻的 s1 始終小等於被修改的 s1|s(且每個數隻被貢獻出去一次),所以可以從大往小列舉 s1, s2
#include #include #include using namespace std;
typedef long long ll;
const int pri[8] = ;
const int inf = 260;
int n; ll mod;
ll f[inf][inf], f1[inf][inf], f2[inf][inf];
struct node
int main()
} if (i==n-1 || p[i].p==1 || p[i].p!=p[i+1].p)
}} }
ll ans = 0;
/* for (int s1=0; s1<=3; s1++)
for (int s2=0; s2<=3; s2++)
printf("f[%d][%d]=%lld\n", s1, s2, f[s1][s2]);*/
for (int s1=0; s1<=255; s1++)
for (int s2=0; s2<=255; s2++) add(ans, f[s1][s2]);
printf("%lld\n", ans);
}
題解 NOI2015壽司晚宴
想好久啊 不敢寫啊 但果然人還是應當勇敢自信,只有堅定地去嘗試,才會知道最後的結果。1a真的太開心啦,不過好像我的做法還是比較複雜的樣子 理解起來應該算是比較容易好懂的型別,大家可以參考一下思路 首先我們先考慮一下簡單的30分演算法 30以內的質數只有十個左右,可以利用狀壓表示出兩個人所選擇的集合,...
NOI 2015 壽司晚宴
description 為了慶祝 noi 的成功開幕,主辦方為大家準備了一場壽司晚宴。小 g 和小 w 作為參加 noi 的選手,也被邀請參加了壽司晚宴。在晚宴上,主辦方為大家提供了 n 1 種不同的壽司,編號 1,2,3,n 1,其中第 i 種壽司的美味度為 i 1 即壽司的美味度為從 2 到 n...
NOI 2015 壽司晚宴
4197 noi2015 壽司晚宴 time limit 10 sec memory limit 512 mb submit 813 solved 508 submit status discuss description 為了慶祝 noi 的成功開幕,主辦方為大家準備了一場壽司晚宴。小 g 和小 ...