NOIP模拟测试12
I boom boom boom
Problem A:斐波那契(fibonacci)
题目名字就叫fibonacci,肯定要往这个方向想。
手玩那棵树,可以发现儿子与父亲的差是小于儿子的最大的fib数列的项。
开个set,一边用x减它能减的最大的fib的项来找爸爸一边往set里塞,然后一边减y一边在set里查,查见了输出就完事了。
不必担心时间复杂度问题,fib增长飞快,fib[60]就已经超过1e13了。
gmk手写hashtable太勇了(
1 #include <bits/stdc++.h> 2 3 long long fib[65], x, y; 4 int m; 5 6 signed main() { 7 fib[1] = fib[2] = 1; 8 for (int i = 3; i <= 60; i++) 9 fib[i] = fib[i - 1] + fib[i - 2]; 10 scanf("%d", &m); 11 for (int i = 1; i <= m; i++) { 12 scanf("%lld%lld", &x, &y); 13 std::set<long long> s; 14 s.insert(x); 15 for (int j = 60; j >= 1; j--) { 16 if (x > fib[j]) 17 x -= fib[j], s.insert(x); 18 if (x == 1) { 19 s.insert(1); 20 break; 21 } 22 } 23 if (s.count(y)) { 24 printf("%lld\n", y); 25 continue; 26 } 27 for (int j = 60; j >= 1; j--) { 28 if (y > fib[j]) { 29 y -= fib[j]; 30 if (s.count(y)) { 31 printf("%lld\n", y); 32 break; 33 } 34 } 35 } 36 } 37 return 0; 38 }
Problem B:数颜色
看到这题就想到了Luogu P1903 数颜色,满脑子都是莫队,我的脑子再也容不下其他的想法(
光速码一个带修莫队,然而由于太久没写,出了很大的偏差。。。。先想了大约半小时,然后调调调,对了个拍感觉没啥问题,不过耗时太多了,已经9:50了。。。期望得分60
然而T成40????
GMK怒斥,块长开了$ \sqrt N $,必死无疑
猛然惊醒,带修莫队最优块长是$ N ^ {\frac{2}{3}}$。。。
带修莫队最优块长是$ N ^ {\frac{2}{3}}$!!
带修莫队最优块长是$ N ^ {\frac{2}{3}}$ !!!
写这种带根号的东西的时候多试试块长。。。20分白丢了。。。。。
1 #include <bits/stdc++.h> 2 3 const int N = 3e5 + 233; 4 int n, m, a[N], cnt[N], blk, pos[N], ans[N], ncnt, ccnt, ch[N]; 5 struct Node { 6 int l, r, t, c, id; 7 friend bool operator <(Node a, Node b) { 8 if (pos[a.l] != pos[b.l]) return pos[a.l] < pos[b.l]; 9 if (pos[a.r] != pos[b.r]) return pos[a.r] < pos[b.r]; 10 else return a.t < b.t; 11 } 12 } nd[N]; 13 14 inline int R() { 15 int a = 0; char c = getchar(); 16 while (!isdigit(c)) c = getchar(); 17 while (isdigit(c)) a = a * 10 + c - '0', c = getchar(); 18 return a; 19 } 20 21 void Change(int t, int l, int r) { 22 if (ch[t] + 1 == l) 23 ++cnt[a[l - 1]], --cnt[a[l]]; 24 if (ch[t] == r) 25 ++cnt[a[r + 1]], --cnt[a[r]]; 26 std::swap(a[ch[t]], a[ch[t] + 1]); 27 } 28 29 inline void Add(int x) {cnt[x]++;} 30 31 inline void Del(int x) {cnt[x]--;} 32 33 signed main() { 34 n = R(), m = R(); 35 for (int i = 1; i <= n; i++) a[i] = R(); 36 for (int i = 1, op; i <= m; i++) { 37 op = R(); 38 if (op == 1) { 39 int l = R(), r = R(), c = R(); 40 nd[++ncnt] = {l, r, ccnt, c, ncnt}; 41 } else { 42 ch[++ccnt] = R(); 43 } 44 } 45 blk = pow(n, 0.666666); 46 for (int i = 1; i <= n; i++) 47 pos[i] = (i - 1) / blk + 1; 48 std::sort(nd + 1, nd + 1 + ncnt); 49 for (int i = 1, l = 1, r = 0, tim = 0; i <= ncnt; i++) { 50 while (tim < nd[i].t) Change(++tim, l, r); 51 while (tim > nd[i].t) Change(tim--, l, r); 52 while (l < nd[i].l) Del(a[l++]); 53 while (l > nd[i].l) Add(a[--l]); 54 while (r < nd[i].r) Add(a[++r]); 55 while (r > nd[i].r) Del(a[r--]); 56 ans[nd[i].id] = cnt[nd[i].c]; 57 } 58 for (int i = 1; i <= ncnt; i++) 59 printf("%d\n", ans[i]); 60 return 0; 61 }
正解有两种,数据结构学傻了的动态开点线段树和二分我选择二分
动态开点线段树好像空间有点问题,TKJ开了迫真内存池其实只是内存回收,我还是写二分了
开个vector存每种颜色存在的位置,修改直接二分查见修改位置暴力改,查询仍旧二分查位置,不多谈了。不过要注意查询的时候一个lower_bound一个upper_bound。
谨防数据结构学傻。。。。别让你的思维被那几个sqrt/log锢住。。。。
1 #include <bits/stdc++.h> 2 3 const int N = 3e5 + 5; 4 int n, m, a[N]; 5 std::vector<int> v[N]; 6 7 inline int R() { 8 int a = 0; char c = getchar(); 9 while (!isdigit(c)) c = getchar(); 10 while (isdigit(c)) a = a * 10 + c - '0', c = getchar(); 11 return a; 12 } 13 14 signed main() { 15 n = R(), m = R(); 16 for (int i = 1; i <= n; i++) { 17 a[i] = R(); 18 v[a[i]].push_back(i); 19 } 20 for (int i = 1, op; i <= m; i++) { 21 op = R(); 22 if (op == 1) { 23 int l = R(), r = R(), c = R(); 24 int p = std::lower_bound(v[c].begin(), v[c].end(), l) - v[c].begin(); 25 int q = std::upper_bound(v[c].begin(), v[c].end(), r) - v[c].begin(); 26 printf("%d\n", q - p); 27 } else { 28 int x = R(); 29 if (a[x] != a[x + 1]) { 30 int p = std::lower_bound(v[a[x]].begin(), 31 v[a[x]].end(), x) - v[a[x]].begin(); 32 int q = std::lower_bound(v[a[x + 1]].begin(), 33 v[a[x + 1]].end(), x + 1) - v[a[x + 1]].begin(); 34 v[a[x]][p]++, v[a[x + 1]][q]--; 35 std::swap(a[x], a[x + 1]); 36 } 37 } 38 } 39 return 0; 40 }
Problem C:分组
考试的时候时间不够了,而且读题读错了。。。。可见置顶计数器。
首先T2时间拖太久,心态有点爆炸了,这次考试的时间安排是有问题的。。。。
然后是读了题后我就开始想,这个团体该咋分啊,然后我就想。。。想了20min左右时间不够了,只能printf("1\n\n")了。。。
考完听讲题才看见K只能等于1或2。。。。。
少星际,多读题。。。多看看数据范围没坏处。。。。
K=1时显然,只能是把1-n分成几个连续的区间,直接单调地扫一遍就完事了。但有一个点,就是关于如何维护冲突。一看这数据范围,枚举平方的话撑死不到5122。我们维护一个是否出现过的数组vis[],每有一个新的数就枚举幂k,看$k^2 - a_i$是否出现过。这样就把判断的复杂度降下来了。
注意,为了得到字典序最小的答案,我们从后往前枚举,这样能使分的位置尽量考前。
1 namespace Subtask1 { 2 signed QAQ() { 3 for (int i = n, j = n; i;) { 4 for (bool gg = 0; j; j--) { 5 for (int k = 1; k * k - a[j] < N; k++) { 6 if (k * k - a[j] <= 0) continue; 7 if (vis[k * k - a[j]]) {gg = 1; break;} 8 } 9 if (gg) break; 10 vis[a[j]] = 1; 11 } 12 if (!j) break; 13 brk[++m] = j; 14 for (; i > j; i--) vis[a[i]] = 0; 15 } 16 printf("%d\n", m + 1); 17 for (int i = m; i >= 1; i--) 18 printf("%d ", brk[i]); 19 puts(""); 20 return 0; 21 } 22 }
K=2时我们可以在每个区间放冲突兔子了。如果把冲突兔子互相连边,区间实际上形成了二分图。
你暴力染色check肯定不行,$N^2$复杂度爆炸 然而数据太水,HZ有人这么A了
我反正看到这玩意就想起的不是关押罪犯,而是Luogu P2024 食物链。这是一个拓展域冰茶姬的板子。
我最开始是很懵的,gmk画图给我解了惑。我们把每只兔子拆开,冲突兔子相连,当达到什么的时候会冲突呢?就是兔子的两个点属于同一集合的时候。这个自己画图多膜膜你%你出来。
于是我们就在Subtask1的基础上,把这玩意加进去。
1 namespace Subtask2 { 2 int fa[N << 1], mx; 3 bool sp[N << 1], vis[N << 1]; 4 5 int get(int x) { 6 return fa[x] == x ? x : fa[x] = get(fa[x]); 7 } 8 9 void merge(int x, int y) { 10 x = get(x), y = get(y); 11 fa[x] = y; 12 } 13 14 void QAQ() { 15 for (int i = 1; i <= n; i++) mx = std::max(mx, a[i]); 16 for (int i = 1; i <= 2 * mx; i++) fa[i] = i; 17 for (int i = n; i >= 1; i--) { 18 for (int j = 2; j <= 512; j++) { 19 if (j * j - a[i] <= 0) continue; 20 if (vis[j * j - a[i]]) { 21 merge(a[i], j * j - a[i] + mx); 22 merge(a[i] + mx, j * j - a[i]); 23 } 24 } 25 if (get(a[i]) == get(a[i] + mx)) { 26 brk[++m] = i; 27 for (int j = i; j <= (m == 1 ? n : brk[m - 1]); j++) 28 fa[a[j]] = a[j], fa[a[j] + mx] = a[j] + mx, vis[a[j]] = 0; 29 } 30 vis[a[i]] = 1; 31 } 32 } 33 }
然而有一个问题,有重复兔子啊。。。当然不是所有重复兔子都要背锅,如果2倍是完全平方数就要背锅了。我们必须把她们放在两组。
大力特判,一个背锅兔子出现两次就不能插入敌对兔子了,放不下。
调试n年。。。。。。。。。
详情请见计数器。
1 namespace Subtask2 { 2 int get(int x) { 3 return fa[x] == x ? x : fa[x] = get(fa[x]); 4 } 5 6 void merge(int x, int y) { 7 x = get(x), y = get(y); 8 if (x != y) fa[x] = y; 9 } 10 11 signed QAQ() { 12 for (int i = 2; i <= 512; i++) 13 if (!((i * i) & 1)) sp[(i * i) / 2] = 1; 14 for (int i = 1; i <= n; i++) mx = std::max(mx, a[i]); 15 for (int i = 1; i <= 2 * mx; i++) fa[i] = i; 16 for (int i = n; i >= 1; i--) { 17 for (int j = 2; j <= 512; j++) { 18 if (j * j - a[i] <= 0) continue; 19 if (j * j == 2 * a[i]) continue; 20 if (vis[j * j - a[i]]) { 21 if (sp[j * j - a[i]] && vis[j * j - a[i]] >= 2) { 22 merge(a[i], a[i] + mx); 23 break; 24 } 25 merge(a[i], j * j - a[i] + mx); 26 merge(a[i] + mx, j * j - a[i]); 27 } 28 } 29 if (sp[a[i]] && vis[a[i]]) { 30 if (vis[a[i]] >= 2) merge(a[i], a[i] + mx); 31 else for (int j = 2; j <= 512; j++) { 32 if (j * j - a[i] <= 0) continue; 33 if (j * j == 2 * a[i]) continue; 34 if (vis[j * j - a[i]]) { 35 merge(a[i], a[i] + mx); 36 break; 37 } 38 } 39 } 40 if (get(a[i]) == get(a[i] + mx)) { 41 brk[++m] = i; 42 for (int j = i; j <= (m == 1 ? n : brk[m - 1]); j++) 43 fa[a[j]] = a[j], fa[a[j] + mx] = a[j] + mx, vis[a[j]] = 0; 44 } 45 vis[a[i]]++; 46 } 47 printf("%d\n", m + 1); 48 for (int i = m; i >= 1; i--) 49 printf("%d ", brk[i]); 50 printf("\n"); 51 return 0; 52 } 53 }
完全代码:
1 #include <bits/stdc++.h> 2 3 const int N = 131072 * 2 + 233; 4 int n, k, a[N], m, brk[N], fa[N << 1], mx, vis[N << 1]; 5 bool sp[N << 1]; 6 7 namespace Subtask1 { 8 signed QAQ() { 9 for (int i = n, j = n; i;) { 10 for (bool gg = 0; j; j--) { 11 for (int k = 1; k * k - a[j] < N; k++) { 12 if (k * k - a[j] <= 0) continue; 13 if (vis[k * k - a[j]]) {gg = 1; break;} 14 } 15 if (gg) break; 16 vis[a[j]] = 1; 17 } 18 if (!j) break; 19 brk[++m] = j; 20 for (; i > j; i--) vis[a[i]] = 0; 21 } 22 printf("%d\n", m + 1); 23 for (int i = m; i >= 1; i--) 24 printf("%d ", brk[i]); 25 puts(""); 26 return 0; 27 } 28 } 29 30 namespace Subtask2 { 31 int get(int x) { 32 return fa[x] == x ? x : fa[x] = get(fa[x]); 33 } 34 35 void merge(int x, int y) { 36 x = get(x), y = get(y); 37 if (x != y) fa[x] = y; 38 } 39 40 signed QAQ() { 41 for (int i = 2; i <= 512; i++) 42 if (!((i * i) & 1)) sp[(i * i) / 2] = 1; 43 for (int i = 1; i <= n; i++) mx = std::max(mx, a[i]); 44 for (int i = 1; i <= 2 * mx; i++) fa[i] = i; 45 for (int i = n; i >= 1; i--) { 46 for (int j = 2; j <= 512; j++) { 47 if (j * j - a[i] <= 0) continue; 48 if (j * j == 2 * a[i]) continue; 49 if (vis[j * j - a[i]]) { 50 if (sp[j * j - a[i]] && vis[j * j - a[i]] >= 2) { 51 merge(a[i], a[i] + mx); 52 break; 53 } 54 merge(a[i], j * j - a[i] + mx); 55 merge(a[i] + mx, j * j - a[i]); 56 } 57 } 58 if (sp[a[i]] && vis[a[i]]) { 59 if (vis[a[i]] >= 2) merge(a[i], a[i] + mx); 60 else for (int j = 2; j <= 512; j++) { 61 if (j * j - a[i] <= 0) continue; 62 if (j * j == 2 * a[i]) continue; 63 if (vis[j * j - a[i]]) { 64 merge(a[i], a[i] + mx); 65 break; 66 } 67 } 68 } 69 if (get(a[i]) == get(a[i] + mx)) { 70 brk[++m] = i; 71 for (int j = i; j <= (m == 1 ? n : brk[m - 1]); j++) 72 fa[a[j]] = a[j], fa[a[j] + mx] = a[j] + mx, vis[a[j]] = 0; 73 } 74 vis[a[i]]++; 75 } 76 printf("%d\n", m + 1); 77 for (int i = m; i >= 1; i--) 78 printf("%d ", brk[i]); 79 printf("\n"); 80 return 0; 81 } 82 } 83 84 signed main() { 85 scanf("%d%d", &n, &k); 86 for (int i = 1; i <= n; i++) scanf("%d", a + i); 87 if (k == 1) return Subtask1::QAQ(); 88 else return Subtask2::QAQ(); 89 }