考了上下午兩場六題,應該是模擬noip的。數組
第三題過於難,考的奇葩數論。T5T6都簡單了些。ide
得分100 + 100 + 40 + 100 + 0 + 30 = 370大數據
簡單分析一下,主要失誤在於T5。優化
三個100就不說,T3盡力了。T6是相似魔法森林的一道題,是個刪邊維護生成樹的套路,沒想出來。spa
T5顯然在個人能力範圍以內,爆0。code
大意:一個寂寞堆的性質是:子節點不大於父節點 && 左子樹每一個點都不大於柚子樹。blog
給你一個滿二叉樹,問至少改變幾個數能使它變成寂寞的。ip
當時我滿腦子都是樹形DP,區間DP,可是怎麼想都不對...get
而後發現能夠把每一個點之間的關係作成圖,不知足的條件做爲邊,而後跑最小點覆蓋。string
而後發現這TM不是二分圖啊......
而後又去想怎麼在這個二叉樹上面DP,沒搞出來......
而後準備把小數據特判得30分,可是由於我愚蠢的輸出了樹中節點個數致使這30分也涼了。
正解:注意上面,把每一個點的關係作成圖。
實際上這個圖的拓撲序惟一,由於任意兩個點的大小均可以藉由lca得出。
因此按照逆拓撲序求最長不升子序列就OK了。
拓撲序在這裏就是中右左序。
接着講一下T6的套路:題意:
給你n個點,q次操做,有加邊,刪邊,查詢連通性。
且每兩點的邊至多隻會被加/刪一次。
n<=400,q<=100000
解:
考慮暴力,顯然是每次DFS。這樣作是qm的,m上限有n²,因此是qn²,能拿30分。
而後咱們發現這個n很小,有什麼用呢?不知道。
而後咱們考慮轉圖爲樹,那麼只需維護生成樹便可。
維護哪些生成樹呢?刪邊時間最靠後的!
由於若是走一些刪邊時間靠前的邊能到達,那麼這個生成樹也必定可達。
若是這個都不可達,那麼刪邊時間靠前的更不可達。
而後,遍歷樹是n的,因而時間被壓到了qn,4e7能夠過。
刪邊的時候若是還在就刪。加邊的時候,若是連通就去掉刪除時間最先的。查詢直接DFS。
實際操做上,因爲是森林因此不須要vis數組。記錄父親便可。
用vector代替鄰接表存邊,以節省遍歷被刪的邊的時間,作到O(n)。
1 #include <cstdio> 2 #include <vector> 3 #include <algorithm> 4 #include <cstring> 5 6 const int N = 405, INF = 0x3f3f3f3f, M = 100010; 7 8 std::vector<int> G[N]; 9 char c[M][10]; 10 int del[N][N], da, db, xx[M], yy[M]; 11 12 bool DFS(int x, int t, int f) { 13 //printf("DFS : x = %d \n", x); 14 if(x == t) { 15 return 1; 16 } 17 for(int i = 0; i < G[x].size(); i++) { 18 //printf("G[%d][%d] = %d \n", x, i, G[x][i]); 19 int y = G[x][i]; 20 if(y != f && DFS(y, t, x)) { 21 return 1; 22 } 23 } 24 return 0; 25 } 26 27 bool getmin(int x, int T, int f) { 28 if(x == T) { 29 return 1; 30 } 31 for(int i = 0; i < G[x].size(); i++) { 32 int y = G[x][i]; 33 if(y == f) { 34 continue; 35 } 36 int t = getmin(y, T, x); 37 if(t && (del[x][y] <= del[da][db])) { // error : < 38 da = x; 39 db = y; 40 } 41 if(t) { 42 return 1; 43 } 44 } 45 return 0; 46 } 47 48 int main() { 49 freopen("fool3.in", "r", stdin); 50 freopen("my.out", "w", stdout); 51 int n, m; 52 scanf("%d%d", &n, &m); 53 int x, y; 54 memset(del, 0x3f, sizeof(del)); 55 for(int i = 1; i <= m; i++) { 56 scanf("%s", c[i]); 57 scanf("%d%d", &xx[i], &yy[i]); 58 if(c[i][0] == 'd') { 59 del[xx[i]][yy[i]] = del[yy[i]][xx[i]] = i; 60 } 61 } 62 63 for(int i = 1; i <= m; i++) { 64 x = xx[i]; 65 y = yy[i]; 66 if(c[i][0] == 'd') { // del 67 for(int i = 0; i < G[x].size(); i++) { 68 if(G[x][i] == y) { 69 std::swap(G[x][i], G[x][G[x].size() - 1]); 70 G[x].pop_back(); 71 break; 72 } 73 } 74 for(int i = 0; i < G[y].size(); i++) { 75 if(G[y][i] == x) { 76 std::swap(G[y][i], G[y][G[y].size() - 1]); 77 G[y].pop_back(); 78 break; 79 } 80 } 81 } 82 else if(c[i][1] == 'd') { // add 83 getmin(x, y, 0); 84 if(del[x][y] <= del[da][db]) { 85 if(da) { 86 da = db = 0; 87 continue; 88 } 89 G[x].push_back(y); 90 G[y].push_back(x); 91 continue; 92 } 93 for(int i = 0; i < G[da].size(); i++) { 94 if(G[da][i] == db) { 95 std::swap(G[da][i], G[da][G[da].size() - 1]); 96 G[da].pop_back(); 97 break; 98 } 99 } 100 for(int i = 0; i < G[db].size(); i++) { 101 if(G[db][i] == da) { 102 std::swap(G[db][i], G[db][G[db].size() - 1]); 103 G[db].pop_back(); 104 break; 105 } 106 } 107 da = db = 0; 108 G[x].push_back(y); 109 G[y].push_back(x); 110 } 111 else { // ask 112 printf("%d\n", DFS(x, y, 0)); 113 } 114 } 115 116 return 0; 117 }
時間卡的很緊,0.9s過的大數據。能夠用LCT優化到qlog²n,那就是魔法森林了。
總結:初級的題沒有失誤很好;T3,T5這種難度的儘可能爭取滿分,沒把握就面向數據,反正部分分必定要拿穩。若是時間還多,暴力又打完了,檢查一萬遍了,儘可能爆肝正解。說不定靈光一閃就A了;不要太自信,對拍是必要的。沒有對拍就手造10min的數據。
[update]:T3簡直天秀,真·神奇解法,天下第一。
以後填坑。