牛客小白月赛12题解简录

题目传送 

 

官方题解传送

 

 1 #include <cstdio>
 2 #include <iostream>
 3 #include <algorithm>
 4 using namespace std;
 5  
 6 const int maxm = 1e5 + 5;
 7 int n, m, ans;
 8 pair<int, int> p[maxm];
 9  
10 int main() {
11     ios_base::sync_with_stdio(0);
12     cin.tie(0);
13      
14     cin >> n >> m;
15     for (int i = 1; i <= m; i++) {
16         cin >> p[i].first >> p[i].second;
17     }
18     p[++m] = {n + 1, n + 1};
19     sort(p + 1, p + 1 + m);
20     for (int i = 1, last = 1, pos; i <= m; i = pos + 1){
21         if (p[i].first > last) {
22             cout << -1 << endl;
23             return 0;
24         }
25         pos = i;
26         int tmp = p[pos].second;
27         while (pos < m && p[pos + 1].first <= last) {
28             tmp = max(tmp, p[++pos].second);
29         }
30         last = tmp + 1;
31         ans++;
32         if (last > n)    break;
33     }
34     cout << ans << endl;
35     return 0;
36 }
A.贪心乱搞

 

 1 #include <cstdio>
 2 #include <cmath>
 3  
 4 typedef long long ll;
 5 int T;
 6 ll a, b, p;
 7  
 8 inline ll ksc(ll a, ll b, ll mod) {
 9     return ((a * b - (ll)((long double)a / mod * b) * mod) % mod + mod) % mod;
10 }
11  
12 inline ll ksm(ll a, ll b, ll p) {
13     ll res = 1ll;
14     for (; b; b >>= 1) {
15         if (b & 1)    res = ksc(res, a, p);
16         a = ksc(a, a, p);
17     }
18     return res;
19 }
20  
21 int main() {
22     for (scanf("%d", &T); T; T--) {
23         scanf("%lld %lld %lld", &a, &b, &p);
24         printf("%lld\n", ksm(a % p, b, p));
25     }
26 }
B.快速幂快速乘板子题

 

 1 #include <cstdio>
 2 
 3 typedef long long ll;
 4 const int maxn = 13e6 + 5;
 5 const int mod = 1e9 + 7;
 6 int n, primes[maxn], tot, val[maxn];
 7 ll ans;
 8 bool vis[maxn];
 9 
10 int ksm(int a, int b) {
11     int res = 1ll;
12     for (; b; b >>= 1) {
13         if (b & 1)    res = (ll)res * a % mod;
14         a = (ll)a * a % mod;
15     }
16     return res;
17 }
18 
19 void pre(int n) {
20     val[1] = 1;
21     for (int i = 2; i <= n; i++) {
22         if (!vis[i]) {
23             val[i] = ksm(i, n);
24             primes[++tot] = i;
25         }
26         for (int j = 1; j <= tot && primes[j] * i <= n; j++) {
27             vis[primes[j] * i] = true;
28             val[primes[j] * i] = (ll)val[primes[j]] * val[i] % mod;
29             if (i % primes[j] == 0)    break;
30         }
31     }
32 }
33 
34 int main() {
35     scanf("%d", &n);
36     pre(n);
37     for (int i = 1; i <= n; i++)
38         ans = ans ^ val[i];
39     printf("%lld\n", ans);
40     return 0;
41 }
C.线性筛暴力

 

D题不会推式子嘤。反演什么的更不敢试了,欧拉累加能过就行了(逃

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <cctype>
 4 
 5 template <typename T> void read(T &x) {
 6     x = 0;
 7     int s = 1, c = getchar();
 8     for (; !isdigit(c); c = getchar())
 9         if (c == '-')    s = -1;
10     for (; isdigit(c); c = getchar())
11         x = x * 10 + c - 48;
12 }
13 
14 template <typename T> void write(T x) {
15     if (x < 0)    x = -x, putchar('-');
16     if (x > 9)    write(x / 10);
17     putchar(x % 10 + '0');
18 }
19 
20 template <typename T> void writeln(T x) {
21     write(x);
22     puts("");
23 }
24 
25 const int maxn = 1e6 + 5;
26 int n;
27 int phi[maxn], primes[maxn], tot;
28 bool vis[maxn];
29 long long val[maxn];
30 
31 void euler(int n) {
32     phi[1] = 1;
33     for (int i = 2; i <= n; i++) {
34         if (!vis[i]) {
35             primes[++tot] = i;
36             phi[i] = i - 1;
37         }
38         for (int j = 1; j <= tot && primes[j] * i <= n; j++) {
39             vis[primes[j] * i] = true;
40             phi[primes[j] * i] = phi[i] * (i % primes[j] ? primes[j] - 1 : primes[j]);
41             if (i % primes[j] == 0)    break;
42         }
43     }
44 }
45 
46 void solve() {
47     for (int d = 1; d <= n; d++) {
48         val[d]++;
49         for (int k = 2; k * d <= n; k++) {
50             val[k * d] += (long long)phi[k] * k / 2;
51         }
52     }
53     for (int i = 1; i <= n; i++) {
54         writeln(val[i]);
55     }
56 }
57 
58 int main() {
59     read(n);
60     euler(n);
61     solve();
62     return 0;
63 }
D.数学推gcd相关公式

 

 1 #include <cstdio>
 2 
 3 const int maxn = 2e5 + 5;
 4 int N, K, a[maxn];
 5 
 6 bool ok(int len) {
 7     long long res = 0;
 8     for (int i = 1; i <= N; i++)
 9         res += a[i] / len;
10     return res >= K;
11 }
12 
13 int main() {
14     scanf("%d %d", &N, &K);
15     for (int i = 1; i <= N; i++)
16         scanf("%d", &a[i]);
17     int l = 1, r = 1e9, ans;
18     while (l <= r) {
19         int mid = (l + r) >> 1;
20         if (ok(mid)) {
21             ans = mid;
22             l = mid + 1;
23         } else    r = mid - 1;
24     }
25     printf("%d\n", ans);
26     return 0;
27 }
E.二分裸题

 

F题说一说。

无脑树状数组显然会爆时间,命题人的方法是sqrt一个块作为界限,对于小复杂度的进行暴力插,大复杂度修改值的记录在桶里,然后每次询问时询问完树状数组再暴力扫一遍块里我们还应该加上的部分。温和地缓解了复杂度压力吧。

 1 #include <cstdio>
 2 #include <cctype>
 3 #include <cmath>
 4 
 5 typedef long long ll;
 6 const int maxn = 1e5 + 5;
 7 int N, M, T;
 8 ll F[maxn], val[maxn];
 9 
10 template <typename T> void read(T &x) {
11     x = 0;
12     int s = 1, c = getchar();
13     for (; !isdigit(c); c = getchar())
14         if (c == '-')    s = -1;
15     for (; isdigit(c); c = getchar())
16         x = x * 10 + c - 48;
17     x *= s;
18 }
19 
20 template <typename T> void write(T x) {
21     if (x < 0)    x = -x, putchar('-');
22     if (x > 9)    write(x / 10);
23     putchar(x % 10 + '0');
24 }
25 
26 template <typename T> void writeln(T x) {
27     write(x);
28     puts("");
29 }
30 
31 void Add(int x, int val) {
32     for (; x <= N; x += x&-x)
33         F[x] += val;
34 }
35 
36 ll Query(int x) {
37     ll ret = 0;
38     for (; x; x -= x&-x)
39         ret += F[x];
40     return ret;
41 }
42 
43 ll cal(int n) {
44     ll ret = Query(n);
45     for (int i = 1; i <= T; i++) {
46         ret += val[i] * (n / i);
47     }
48     return ret;
49 }
50 
51 int main() {
52     read(N), read(M);
53     for (T = sqrt(N); M; M--) {
54         int op, a, b;
55         read(op), read(a), read(b);
56         if (op == 1) {
57             if (a > T) {
58                 for (int i = a; i <= N; i += a)
59                     Add(i, b);
60             } else {
61                 val[a] += b;
62             }
63         } else {
64             writeln(cal(b) - cal(a - 1));
65         }
66     }
67     return 0;
68 }
F.划界均摊法

我又试了试,验题人的做法,然后最后一个点T了……也贴这吧。思路是树状数组只修改这个数字D,不用修改D的倍数,然后询问时用整除分块+树状数组查询前缀和的方式。

update:验题人本人代码也被T了233……

 1 #include <cstdio>
 2  
 3 typedef long long ll;
 4 const int maxn = 1e5 + 5;
 5 int N, M;
 6 ll F[maxn];
 7  
 8 void Add(int x, int val) {
 9     for (; x <= N; x += x&-x)
10         F[x] += val;
11 }
12  
13 ll Query(int x) {
14     ll ret = 0;
15     for (; x; x -= x&-x)
16         ret += F[x];
17     return ret;
18 }
19  
20 ll cal(int n) {
21     ll ret = 0;
22     for (int l = 1, r; l <= n; l = r + 1) {
23         r = n / (n / l);
24         ret += (Query(r) - Query(l - 1)) * (n / l);
25     }
26     return ret;
27 }
28  
29 int main() {
30     for (scanf("%d %d", &N, &M); M; M--) {
31         int op, a, b;
32         scanf("%d %d %d", &op, &a, &b);
33         if (op == 1) {
34             Add(a, b);
35         } else {
36             printf("%lld\n", cal(b) - cal(a - 1));
37         }
38     }
39     return 0;
40 }
F.整除分块+树状数组

 

 1 #include <cstdio>
 2 #include <algorithm>
 3 
 4 long long a, b;
 5 char s[100005];
 6 
 7 int main() {
 8     scanf("%lld %lld %s", &a, &b, s);
 9     printf("%lld\n", std::__gcd(a, b));
10 }
G.更相减损的原理

 

H题说一说。

对所有的子树加操作,考虑树剖。把树建成以后dfs序一下进行转换会使得父子兄弟节点都挨着,这样就好维护了。(树剖是用线段树,本题官解用的差分树状数组。)

离线。之后重读一遍M个操作,如果是操作一,那这个点是新建的,则把它一个人的值清零(操作二会导致之前它还没出现但是却有值);

如果是操作二,则把这个点加个值,因为树状数组维护了前缀和,所以如果查询的话其实他(dfs序)之后的点也加了值,所以差分一下,在孩子以后的点再减去这个不应属于它们的值;

操作三询问,直接询问前缀和即可。因为我们差分了,所以这个查询只会包含它本身及其各位祖先节点加过的值,正是所需。

 1 #include <cstdio>
 2 #include <cctype>
 3 #include <vector>
 4 using namespace std;
 5 
 6 template <typename T> void read(T &x) {
 7     x = 0;
 8     int s = 1, c = getchar();
 9     for (; !isdigit(c); c = getchar())
10         if (c == '-')    s = -1;
11     for (; isdigit(c); c = getchar())
12         x = x * 10 + c - 48;
13     x *= s;
14 }
15 
16 template <typename T> void write(T x) {
17     if (x < 0)    x = -x, putchar('-');
18     if (x > 9)    write(x / 10);
19     putchar(x % 10 + '0');
20 }
21 
22 template <typename T> void writeln(T x) {
23     write(x);
24     puts("");
25 }
26 
27 const int maxn = 1e5 + 5, maxm = 4e5 + 5;
28 int M, tot, time, DFN[maxn], SIZE[maxn];
29 vector<int> v[maxn];
30 
31 struct Operation {
32     int op, i, a;
33 }OP[maxm];
34 
35 struct BIT {
36     int F[maxn];
37     
38     void Update(int pos, int val) {
39         for (; pos <= time; pos += pos&-pos)
40             F[pos] += val;
41     }
42     
43     void Add(int l, int r, int val) {
44         Update(l, val);
45         Update(r, -val);
46     }
47     
48     int Query(int pos) {
49         int ret = 0;
50         for (; pos; pos -= pos&-pos)
51             ret += F[pos];
52         return ret;
53     }
54 }T;
55 
56 void dfs(int cur) {
57     DFN[cur] = ++time;
58     SIZE[cur] = 1;
59     for (auto son : v[cur]) {
60         dfs(son);
61         SIZE[cur] += SIZE[son];
62     }
63 }
64 
65 int main() {
66     read(M);
67     for (int i = 1; i <= M; i++) {
68         read(OP[i].op), read(OP[i].i);
69         if (OP[i].op == 1) {
70             v[OP[i].i].push_back(++tot);
71         } else if (OP[i].op == 2) {
72             read(OP[i].a);
73         }
74     }
75     dfs(0);
76     tot = 0;
77     for (int i = 1; i <= M; i++) {
78         if (OP[i].op == 1) {
79             ++tot;
80             T.Add(DFN[tot], DFN[tot] + 1, -T.Query(DFN[tot]));
81         } else if (OP[i].op == 2) {
82             int curnode = OP[i].i;
83             T.Add(DFN[curnode], DFN[curnode] + SIZE[curnode], OP[i].a);
84         } else {
85             writeln(T.Query(DFN[OP[i].i]));
86         }
87     }
88     return 0;
89 }
H.dfs序+差分树状数组

 

 1 #include <cstdio>
 2 #define min(a, b) a < b ? a : b
 3 
 4 const int maxn = 1e5 + 5;
 5 const int maxm = 6e5 + 5;
 6 int N, M, tot, time, ans;
 7 int head[maxn], DFN[maxn], low[maxn];
 8 bool vis[maxn];
 9 struct EDGE {
10     int to, nxt;
11 }e[maxm];
12 
13 void add(int u, int v) {
14     e[++tot].to = v;
15     e[tot].nxt = head[u];
16     head[u] = tot;
17 }
18 
19 void dfs(int cur, int fa) {
20     DFN[cur] = low[cur] = ++time;
21     vis[cur] = true;
22     for (int i = head[cur]; i; i = e[i].nxt) {
23         int son = e[i].to;
24         if (!vis[son]) {
25             dfs(son, cur);
26             low[cur] = min(low[cur], low[son]);
27             if (low[son] > DFN[cur])
28                 ans++;
29         } else if (son != fa) {
30             low[cur] = min(low[cur], DFN[son]);
31         }
32     }
33 }
34 
35 int main() {
36     scanf("%d %d", &N, &M);
37     for (int i = 1; i <= M; i++) {
38         int u, v;
39         scanf("%d %d", &u, &v);
40         add(u, v);
41         add(v, u);
42     }
43     dfs(1, 0);
44     printf("%d\n", M - ans);
45     return 0;
46 }
I.求割边数目

 

J题说一说。

既然只有26个字母,考虑往这上转化。做法是:因为只是子序列就行,所以贪心选取,优先选取前面的位置。O(26*len)的复杂度预处理一个叫“序列自动机”的东西,得出“对于位置i,下一个字母j最早出现在哪个位置”。这样之后每个询问的串,只要跟着自动机跳就行了,复杂度都是O(m)的。

 1 #include <cstdio>
 2 #include <cstring>
 3 
 4 const int maxn = 1e6 + 5;
 5 int n, len;
 6 int Go[maxn][27];
 7 char str[maxn];
 8 char t[maxn];
 9 
10 int main() {
11     scanf("%s", str + 1);
12     len = strlen(str + 1);
13     for (int i = len - 1; ~i; --i) {
14         for (int j = 1; j <= 26; ++j) {
15             Go[i][j] = Go[i + 1][j];
16         }
17         Go[i][str[i + 1] - 'a' + 1] = i + 1;
18     }
19     for (scanf("%d", &n); n; n--) {
20         scanf("%s", t);
21         bool flag = true;
22         for (int i = 0, j = 0; t[i]; ++i) {
23             if (Go[j][t[i] - 'a' + 1]) {
24                 j = Go[j][t[i] - 'a' + 1];
25             } else {
26                 flag = false;
27                 break;
28             }
29         }
30         flag ? puts("Yes") : puts("No");
31     }
32     return 0;
33 }
J.预处理序列自动机

 

转载于:https://www.cnblogs.com/AlphaWA/p/10563993.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值