不提前完成任務的報應就是這樣……昨天晚上熬夜趕題面捏數據,結果早上起來發現各種漏洞,最后兩題還沒時間捏數據了,算是徹底失敗吧= =|||
先把題解寫出來,下午再慢慢捏數據好了。
A
第一題大概意思是維護一個集合,要求實現兩種操作:添加一個元素 和 刪去其中較大的一半(注意是下取整)。最后還要求能夠線性地輸出(雖然排序后再輸出沒有被卡掉……)。
算法一是直接用一棵平衡樹維護,因為每個元素只可能被刪除一次,所以直接暴力刪除就可以。
然而平衡樹常數比較大(雖然我已經沒有時間去造針對平衡樹的數據了……),可以考慮用一個最大堆實現,刪除時直接依次彈出就可以了。
我為正解准備的數據范圍似乎也不怎么合理,只有10^7……其實這個算法是在《算法導論》攤還分析一節中的習題,我們只需要維護一個無序數列,實現一個$O(|S|)$的刪除操作,而每次插入的時候直接將元素附加在數組末尾,就可以在關於操作數呈線性的時間內完成所有操作了。這個同樣可以通過“每個元素只會被刪除一次”來證明——我們可以將每次刪除操作需要的時間附加在刪除的那些元素對應的插入操作上,由於每次刪除的元素個數也是$O(|S|)$,很明顯所有刪除操作附加在插入操作上的時間是常數級別的。
對於無序數列的刪除操作可以在《算法導論》上的“順序統計”一節找到。具體地,我們可以通過類似快速排序的隨機算法在線性的期望時間內求出需要刪除的最小的元素,將比這個元素小的元素全部移動到數組的前半部分就可以了。

2 #include
3
4 const int maxn = 10000003;
5 typedef long long LL;
6
7 int A[maxn], *it = A, N;
8 bool exist[maxn];
9
10 int main(){
11 #ifdef DEBUG
12 freopen( " test.txt ", " r ", stdin);
13 #endif
14 int *i, M, *t, mid, a, x;
15 unsigned rnd, C;
16 scanf( " %d%d%u%d%u ", &N, &M, &rnd, &a, &C);
17 x = a;
18 while(M--){
19 rnd += (rnd << 2) + 1;
20 if(rnd & C)*(it++) = x, x = (LL)x * a % N;
21 else{
22 t = A + (it - A + 1) / 2;
23 std::nth_element(A, t, it);mid = *t;
24 for(i = A;i < t;++i) if(*i >= mid)std::swap(*i, *(--it));
25 it = t;
26 }
27 }
28 printf( " %d\n ", int(it - A));
29 for(i = A;i < it;++i)exist[*i] = true;
30 for( int j = 1;j <= N;++j) if(exist[j])printf( " %d ", j);
31 puts( "");
32 return 0;
33 }
B
這題其實是我把一個idea強行套在了一個經典算法上……所以數據范圍出得有點牽強,非常抱歉……
第一層數據范圍是強行留給只會倍增LCA的同學的(比如一兩個個月前的我)。
第二層數據,由於詢問次數比較大(不過我猜我還是卡不掉倍增……),可以先預處理出dfs序列,構造出ST表來實現$O(1)$查詢。
第三層數據……嗯……說出來你們不要打我啊!= = 我們來看數據中的N,都符合$3 * 2^i$的形式,所以此中必有蹊蹺!觀察那個“強制在線”的遞推式,,
,可以發現$\frac{N}{3}$恰好是$\phi(N)$的表達式。那么遞推式就變成了這樣……
$u_i = X^{\phi(N) * Ans_{i-1}} * X^k * i \mod N$,或$u_i = X^k * i \mod N$。那么我們就可以用Tarjan LCA離線做了……雖然數據范圍很怪異,但是……領會精神,領會精神……
C
我思考了好久的idea竟被出成了這個鬼樣子……= =還是數據范圍的問題,看來不先寫std真是不能亂定數據范圍啊= =
算法一:$$F_i = 1 (i = 0 \mod M \lor i < M);$$
$$F_i = F_{i-1} + F_{i-M}(otherwise)$$
直接遞推,不說了;
算法二:算法一 + 高精度.(其實我實在不想加上這一層數據……)
算法三:不難發現,從M開始,遞推式的選擇是以M為周期的。嘗試把從0開始的數列中的元素每M個一行鋪成矩陣,那么遞推式就可以寫成這樣:
$$F_{i,j} = 1 (i = 0 \lor j = 0)$$
$$F_{i,j} = F_{i-1,j} + F_{i,j-1}$$
發現遞推式很熟悉?沒錯這個數列其實就是組合數……具體來說是$F_{i,j} = \tbinom{i}{i+j} = \frac{(i+j)!}{i! j!}$ 用這里介紹的階乘質因數分解 求組合數就可以了。