我把這題推薦給yyb讓他把這題作它的T2他居然不要QwQ.......
題目大意:
下發八個題目和對應的八份代碼,請構造數據Hack下發代碼。算法
下發代碼用了一些神奇作法實現A + B = C這個操做,因爲 |A|,|B| <= 100,因此暴力for便可。數組
for(int a = -100; a <= 100; a ++) for(int b = -100; b <= 100; b ++) { if(ADD(a ^ b, (a & b) << 1) != a + b){ cout << a << " " << b << endl , exit(0) ; } }
下發代碼是按照邊數分治的作法,當邊數\(\leq 10^6\)時跑\(O(m)\)暴力,當邊數\(>10^6\)時跑\(O(n^2)\)暴力。
先構造一個大小爲\(1000\)的團讓邊數超過限制,而後構造一條鏈每次詢問鏈的兩端。
這樣它的複雜度就變成了\(O(Qn^2)\),\(5sec\)時限妥妥的\(TLE\)。spa
n = 5000 ; Q = 10000 ; cout << n << " " << Q << endl ; for(int i = 2; i <= 1000; i ++) cout << 1 << " " << 1 << " " << i << endl ; cout << 1 << " " << 1 << " " << 1001 << endl ; for(int j = 1002; j <= 5000; j ++) cout << 1 << " " << j - 1 << " " << j << endl ; for(int j = 5001; j <= 10001; j ++) cout <<2<<" "<< 5000 << " " << 1 << endl ;
下發代碼就是聽說當場\(AC\)了[CTSC2018]暴力寫掛的迭代亂搞作法。
迭代的作法是:每次選中一個點,找到其最優勢,而後跳到那個最優勢,接着遞歸加卡時。
首先構造一個答案點,而後想辦法讓迭代的過程當中不碰到那兩個點。
個人作法是建兩顆形態和邊權都同樣的樹,
先固定答案點爲\(x,y\),連邊\((x,z,inf),(y,z,inf),(z,1,-inf)\),\(z\)的做用是使得\(x,y\)深度變爲\(0\)。
而後咱們給其餘全部點找一個迭代最優勢\(c\),連邊:\((1,a,0),(a,b,inf-1),(b,c,inf-1)\)。
爲了保證\(x,y\)不在第一次找點時就被找到,在\(1\)下面連一堆\((t,1,-1)\)的兒子\(t\)便可。
這樣無論從哪裏出發,迭代若干次後都會到達\(c\),而\(x,y\)始終都不會被碰到,天然也就獲得了錯誤答案\(2inf-2\)。code
n = 100000 ; fa[2] = 1 ; e[2] = -inf ; fa[3] = 2 ; e[3] = inf ; fa[4] = 2 ; e[4] = inf ; for(int i = 5; i <= 99997; i ++) fa[i] = 1 , e[i] = -1 ; fa[99998] = 1 ; e[99998] = 0 ; for(int i = 99999; i <= 100000; i ++) fa[i] = i - 1 , e[i] = inf - 1 ; cout << 100000 << endl ; for(int i = 2; i <= n; i ++) cout <<i <<" "<< fa[i] <<" " << e[i] << endl ; for(int i = 2; i <= n; i ++) cout <<i <<" "<< fa[i] <<" " << e[i] << endl ;
先看懂下發程序在幹啥,它維護單調棧,對於每個本質不一樣的最大值,維護最靠前的位置進行轉移。
它是用單調隊列維護的,即若插入決策優於隊尾決策,則把隊尾彈掉。
然而對於\(j\to i\),若決策\(j\)對\(i\)不優,不能說明\(j\)對以後的決策不優,即沒有單調性。
考慮構造一組數據,讓決策點回彈時決策點已經被彈出。
咱們有兩個決策點\(A,B\)和兩個轉移點\(i_1,i_2\),咱們令\(i_1\)的最優決策爲\(B\),\(i_2\)的最優決策爲\(A\)。
因爲\(A,B\)要可以同時存在在單調隊列中,因此有:\(a_A > a_B > a_{i_1,i_2}\)。
那麼咱們要作的就是當加入\(f_B + a_{i_1}\)這個決策時,把\(f_A + a_B\)這個決策彈掉。
形式化的:\(f_A + a_B \ge f_B + a_{i_1}\)、\(f_A + a_B < f_B + a_{i_2}\)。
先考慮\(f_B\),若\(f_B = f_A + a_B\),則第一個限制顯然假了,因此\(f_B\)必定是從更靠前的轉移點轉移過來。
不妨考慮最簡單的一種狀況,即到\(i_1\)時,決策隊列中只有\(A,B\)兩個決策。
要實現這種狀況,最簡單的辦法就是讓以前元素都小於等於\(A\)。
咱們直接讓\(A,B,i_1,i_2\)相鄰,那麼因爲\(a_A > a_B > a_{i_1,i_2}\),因此到了\(A\)時隊列應該是空的。
這種狀況下,\(f_A = f_{A - m} + a_A\)。
咱們已知\(f_B \neq f_A + a_B\),因此\(f_B = f_{B - m} + a_B\) 。
咱們如今的限制變爲:\(f_{A-m} + a_B \ge f_{B - m} + a_{i_1}\)、\(f_{A-m} + a_B < f_{B -m} + a_{i_2}\)。
既然如今的數組形如\(...ABi_1i_2\),一個直接想法就是\(m = 3\)。
可是有一個問題,咱們在程序末尾有一個\(f_{i-m} + max(i-m+1...,i-1,i) \to f_i\)的轉移。
這會致使即時咱們把決策\(A\)彈掉了,\(f_{i_2}\)依舊會用\(f_A\)更新。
解決方案就是讓\(m=4\),把\(A' = A\)設的特別大,而後放形如\(...A'A'ABi_1i_2\)的序列。
因爲\(A'=A\)特別大,那麼最優狀況下必定是把三個\(A\)劃分紅一段,那麼從\(i-m\)轉移必定不優。
如今咱們只須要讓\(x = f_{A-m}\),\(y = f_{B-m}\),而後放\(xyA'A'ABi_1i_2\)就好了。
咱們不妨讓\(x \leq y\),那麼\(f_{B-m} = y\)、\(f_{A-m} = x\)。
因此限制條件變爲:\(x + a_B > y + a_{i_1}\)、\(x + a_B < y + a_{i_2}\),\(x<y<A>B>a_{i_1,i_2}\)。
肯定了上述限制和序列結構\(xyA'A'ABi_1i_2\)後就至關好構造了,手玩一下便可。遞歸
n = 8 ; m = 4 ; cout << n << " " << m << endl ; puts("50 100 1000 1000 1000 500 400 490") ;
選若干區間實際上只須要選兩個區間,選更多的顯然沒用。
觀察一下下發代碼,它是幾個貪心拼在一塊兒。
首先把區間去重變爲左右端點遞增,而後第一個貪心是隨機選區間,這個能夠直接看成沒看見。
先觀察第二個貪心,它是一個不斷縮小區間範圍的算法。
再看一下第三個貪心,它選第二個貪心中答案最大的幾個右端點,暴力\(check\)該右端點的全部區間。
咱們的任務:讓第二個貪心得不到答案,且答案區間的右端點不是第二個貪心中最大的幾個。
令\(n = 10^5\),固定答案區間爲\([n-1,n]\)。
先讓前面的區間長度較小,
而後把第二個貪心右端點\(=n\)的詢問區間打表打出來,發現最小大概\([99995,99998]\)。
任務變爲:讓\(calc(b_n,b_{99998}) < calc(b_n,b_{99997}) < calc(b_n,b_{99996}) < calc(b_n,b_{99995})\)。
先讓\(calc(b_{n-1},b_n) = (10^8)^2 = 10^{16}\)的級別,那麼以前區間的答案不能超過這個數量級。
顯然越大越好處理,那麼咱們讓\(b_{99998}\)到\(b_{99995}\)左端點每次加\(10^7\)。
考慮爲了知足上述不等式,這些區間與\(n\)的交爲多少。
因爲向左移動式,區間並在不斷增長,因此區間交必定要不斷減小。
設\(99998\)與\(100000\)的交爲\(Cross\),咱們考慮每向左一下區間交減小\(1\)。
那麼從\(99999\)向左\(c\)個,則\(calc(b_{99999-c},b_n) = (len_n + 10^7c)(Cross - c)\)。
因此\(f(c) = -10^7c^2 + (10^7Cross - len_n)c\)要單調遞增,構造一下\(Cross = 10^5\)就差很少了。
咱們還不能讓右端點爲\(n\)成爲第二個貪心中的最大值,讓前面的區間答案大概\(10^{14}\)左右數量級便可。隊列
n = 100000 ; m = 10000000 ; L = 1000000000 ; Cross = 100000 ; sl = L - m * 10 ; sr = L ; cout << n << endl ; for(int i = 1; i <= 99994; i ++) cout << i <<" "<<i + m <<endl ; cout << sl - 5*m << " " << sl + Cross - 5 << endl ; cout << sl - 4*m << " " << sl + Cross - 4 << endl ; cout << sl - 3*m << " " << sl + Cross - 3 << endl ; cout << sl - 2*m << " " << sl + Cross - 2 << endl ; cout << sl - 1 << " " << sr - 1 << endl ; cout << sl << " " << sr << endl ;
旋轉卡殼的經典錯誤,旋轉卡殼不能對凸包上的每個點求距離其的最遠點。
構造一個寶石形狀的凸包便可。it
n = 6 ; cout << n << endl ; puts("-8 0") ; puts("-6 -1") ; puts("6 -1") ; puts("8 0") ; puts("6 1") ; puts("-6 1") ;
你發現這貨不就相似什麼珂朵莉樹,每隔\(32\)個操做把相同的段都\(merge\)起來。
先用\(10^4\)個區間賦值操做把整個區間變成互不相同的數,
而後再用\(10^4\)個區間加法操做讓他瘋狂\(for(i=1\to n)\)直接卡爆它。io
n = 10000 ; m = 20000 ; cout << n << " " << m << endl ; for(int i = 1; i <= n; i ++) cout << 0 << " " << i << " " << i << " " << i << endl ; for(int i = 1; i <= n; i ++) cout << 1 << " " << 1 << " " << n << " " << 1 << endl ;
觀察良久發現這個亂搞是這樣的:對每一個點分別找到\(x,y\)最近的\(\frac{60000000}{n}\)個點,而後建最小生成樹。
那麼咱們令\(n = 10^5\),也就是每次找\(x,y\)相鄰的\(60\)個點。
首先建兩個點\((0,0)\)、\((61,61)\)。
接着在它們中間插入\(60\)個\((1,inf)\)、\((inf,1)\)。
而後就作完了?把剩下的點放到\((inf,inf)\)來湊數,最後詢問一下\((0,0)\)和\((61,61)\)就好了。class
n = 100000 ; cout << n << endl ; cout << "0 0" << endl ; cout << "61 61" << endl ; n -= 2 ; for(int i = 1; i <= 600; i ++, -- n) cout << 1 << " 100000" << endl ; for(int i = 1; i <= 600; i ++ , --n) cout << "100000" << " " << 1 << endl ; while(n --) puts("100000 100000") ; Q = 1 ; cout << Q << endl ; puts("1 2") ;