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 }
Fibonacci


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 }
Captian Mo's Algorithm


正解有两种,数据结构学傻了的动态开点线段树和二分我选择二分


动态开点线段树好像空间有点问题,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 }
Binary Serach

 

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 }
Subtask 1

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 }
Subtask2_first

然而有一个问题,有重复兔子啊。。。当然不是所有重复兔子都要背锅,如果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 }
Subtask 2_final

完全代码:

 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 }
考试不读题,爆0两行泪

 

posted @ 2019-08-03 19:03  Gekoo  阅读(166)  评论(0编辑  收藏  举报