LOJ3342 NOI2020 製作菜品

2022-03-12 12:19:35 字數 4580 閱讀 7590

題目鏈結

博主有幸參加了noi2020,考場上的經歷和心得請見這篇文章。這裡就不嘮叨了。

本題的突破口在於\(m\)和\(n\)的關係。也就是資料範圍表裡這些奇怪的限制:\(m=n-1\),\(m\geq n-1\),\(m\geq n-2\)。我們乙個乙個來看。

顯然,\(n\)種原材料,除了在輸出答案時,其他時候它們的原始順序對我們解題沒有任何影響。所以可以先將它們排序。現在假設\(d_1\leq d_2\leq \dots \leq d_n\)。

考慮一種貪心:先用最大的原材料和最小的原材料一起做成第一道菜。然後把它們剩餘的部分,當做一種新的材料,插入回\(d\)序列中,轉化為乙個\(m-1\)道菜的子問題。

這種貪心在\(m=n-1\)時是正確的,而且一定有解。以下是證明:

引理1.1:\(d_1。

證明1.1

反證法,假設\(d_1\geq k\),則\((\sum_^d_i)\geq d_1\times n\geq k\times n\)。又因為\((\sum_^d_i)=m\times k\),所以\(m\times k\geq n\times k\),\(m\geq n\)。與\(m=n-1\)矛盾。故可以證明:\(d_1。

引理1.2:\(d_1+d_n\geq k\)。

證明1.2

反正法,假設\(d_1+d_n,則\((\sum_^d_i)=(d_1+d_n)+(\sum_^d_i)<(n-1)\times k\)。這與\((\sum_^d_i)=m\times k=(n-1)\times k\)矛盾。故可以證明:\(d_1+d_n\geq k\)。

結合引理1.1和引理1.2,我們在貪心時,每次操作,一定會把最小的原材料和最大的原材料都用上,一定能用它們拼成一道菜,並且能把多餘的\(d_1+d_n-k\)作為一種「新的」原材料放回序列中。那麼,每次操作後,\(n\)和\(m\)各減小\(1\),仍然滿足\(m=n-1\)。我們如此歸納下去,直到\(m=1,n=2\)時,直接拼成一道菜即可。

注意,在證明時,我們認為可以允許存在一道菜\(d_i=0\)(否則,\(n\)每次就不一定減少\(1\),而有可能減少\(2\):也就是\(d_1+d_n=k\)的情況)。這樣假設不會影響該做法的正確性,但在輸出答案時要注意判斷。

樸素地實現這一貪心,每次操作後將序列重新排序。時間複雜度\(o(mn\log n)\)。可以用\(\texttt\)優化到\(o(m\log n)\)不過沒有必要。

\(m\geq n\)時,考慮向\(m=n-1\)轉化。

引理2.1:\(d_n\geq k\)。

證明2.1

反證法,假設\(d_n,則\((\sum_^d_i)。又因為\((\sum_^d_i)=m\times k\),所以\(m\times k,\(m。與\(m\geq n\)矛盾。故可以證明:\(d_n\geq k\)。

於是我們每次使用\(d_n\)做一道菜,這樣\(m\)會減少\(1\),\(n\)不變。若干次後,一定能轉化為\(m=n-1\)的情況。然後按上一段所述的方法貪心構造即可。

考慮把手上的原材料,分成兩部分,分別滿足\(m=n-1\)。具體來說,我們要證明:

引理3.1:\(m=n-2\)時,有解當且僅當,存在乙個集合\(s\subsetneq \\),使得\((\sum_d_i)=(|s|-1)\times k\)。

證明3.1

充分性:可以對\(s\)和\(t=\\setminus s\)這兩個集合分別構造方案。根據定義,顯然這兩個集合都滿足:「\(m=n-1\)」。所以一定是有解的。

必要性:考慮建一張\(n\)個點的圖。如果在最終方案下,第\(i\)種原材料和第\(j\)種原材料,曾經共同拼成過一道菜,則在點\(i\)和點\(j\)之間連一條邊。因為總共只有\(m\)道菜,所以最多會連出\(m=n-2\)條邊。因此這張圖一定不連通。此時必然至少有乙個連通塊滿足「\(m=n-1\)」(也就是至少有乙個連通塊是。不可能每個連通塊都有環,否則邊數不夠用了),它就是集合\(s\)。也就是說,有解時,必然存在乙個這樣的集合\(s\)。

如何劃分出這樣的集合\(s\)呢?\(2^n\)列舉肯定不行。考慮dp。

可以設計出這樣乙個樸素的狀態:\(dp[i][j][w]\),表示考慮了前\(i\)種原材料,選出了\(j\)種原材料,它們的質量之和為\(w\),是否存在一種這樣的方案。轉移就考慮下一種原材料選或不選,分別轉移到\(dp[i+1][j+1][w+d_]\)和\(dp[i+1][j][w]\)。最終,如果存在乙個\(j\)使得\(dp[n][j][(j-1)\times k]=1\),則有解,我們順著轉移的過程,反推回去就能得到集合\(s\)。

時間複雜度\(o(n^2\sum d_i)=o(n^3k)\)。

繼續優化,發現我們並不關心\(j\)是多少,只關心\(j\)和\(w\)的關係:也就是\(w-j\times k=-k\)。於是我們可以令每樣物品的權值為\(v_i=d_i-k\)。設計乙個新的狀態:\(dp[i][j]\),表示考慮了前\(i\)種物品,權值和為\(j\),是否存在這樣的方案。考慮下一種原材料選或不選,可以轉移到\(dp[i+1][j+v_]\)和\(dp[i+1][j]\)。最終只要看\(dp[n][-k]\)是否為\(1\)即可。另外,這種dp狀態下,第二維可能是負數(最小為\(-nk\))。所以在實現時,我們把數值統一加上\(nk\)即可。

在新的狀態下,dp的狀態數減小至\(n\times(nk+mk+1)\)個(第一維大小為\(n\),第二維在\([-nk,mk]\))。轉移是\(o(1)\)的。時間複雜度\(o(n^2k)\),還是不足以通過全部資料。

因為dp陣列裡只存\(01\)兩種值,所以考慮用\(\texttt\)優化dp。具體來說,把dp的第二維,看做乙個大小為\(nk+mk+1\)的\(\texttt\),則dp的轉移就相當於將上一階段的\(\texttt\),上它左移\(v_i\)位。即:\(dp[i]=dp[i-1]\operatorname(dp[i-1]\ll v_i)\)。特別地,如果\(v_i\)為負數,要寫成右移\(|v_i|\)位。

時間複雜度\(o(\frac)\),其中\(w=64\)。可以通過本題。

還有乙個小問題。做完這個dp後,只是知道了是否有解。但我們還需要構造出\(s\)集合。不過這也不難,通過現有的dp陣列就能構造出來。考慮定義乙個\(\texttt\)函式,用遞迴實現。保證傳入的\(i,x\)滿足\(dp[i][x]=1\)。顯然,當\(i>0\)時,\(dp[i-1][x]\)和\(dp[i-1][x-v_i]\)必有至少乙個為\(1\)。任選乙個為\(1\)的遞迴下去即可。如果遞迴了\((i-1,x-v_i)\),相當於把原材料\(i\)加入集合\(s\),否則相當於加入另乙個集合。初始時,傳入\(i=n,x=-k\)。遞迴的邊界是\(i=0\)時直接返回。這樣顯然可以構造出滿足我們要求的兩個集合(分別滿足\(m=n-1\))。

總時間複雜度\(o(\frac+mn\log n)\)。

//problem:loj3342

#include using namespace std;

#define pb push_back

#define mk make_pair

#define lob lower_bound

#define upb upper_bound

#define fi first

#define se second

#define sz(x) ((int)(x).size())

typedef unsigned int uint;

typedef long long ll;

typedef unsigned long long ull;

typedef pairpii;

templateinline void ckmax(t& x,t y)

templateinline void ckmin(t& x,t y)

else

} for(int i=1;i<=m;++i)

else

}void solve_case()

if(n<=m+1)

assert(n==m+2);

bas=n*k;// dp陣列為了保證下標不為負而產生的的偏移量

//dp[-x] -> dp[-x+bas]

dp[0].reset();

dp[0][0+bas]=1;

bool flag=0;

for(int i=1;i<=n;++i)

flag=1;

break;

} }if(!flag)

int main()

return 0;

}

3342 NOI2020 製作菜品

題目鏈結 考場上想到 70pts,結果沒清空 沒特判轉移 丟掉 35pts 不那麼顯然,如果 m ge n 1 那麼一定有解。考慮如果只剩下一堆大於 k 的材料,那麼每次我們取乙個,不夠再取另乙個,一定能取完。於是我們只用考慮怎麼消滅小於 k 的材料。因為每次我們可以把最小的那個取走,不夠再從大於 ...

LOJ 2303 NOI2017 蚯蚓排隊

n leq 200000 的 1 leq a i leq 6 的蚯蚓,有三種操作 讓乙隻隊頭蚯蚓接在乙隻隊尾蚯蚓後面 讓一隊蚯蚓從某個蚯蚓後面斷成兩隊 問 給個字串,問他的。算了你們直接看題吧 這什麼沙雕題qaq 所有詢問的串只有 nk 種,把他們全丟進hash裡面就好了。注意雙hash,乙個用來當...

loj 2721 NOI2018 屠龍勇士

有 n 條惡龍,每條惡龍有乙個初始生命值 a i 恢復力 p i 擊殺後會掉落一把劍.初始你有 m 把劍.給出每把劍的攻擊力.現在你打算按照以下方法殺掉所有惡龍 問最小的 x 如果不存在能殺掉所有惡龍的 x 輸出 1 有 t 組資料 n le 10 5,m le 10 5,t le 5,a i le...