2019牛客暑期多校训练营 第四场

应该是目前为止最简单的一场。没来得及做的题待补。

题目链接:https://ac.nowcoder.com/acm/contest/884#question


A:

注意虚树直径即可。

B:

最近多校特别多线性基的题目。这题是线段树维护线性基的交。

  1 /* basic header */
  2 #include <bits/stdc++.h>
  3 /* define */
  4 #define ll long long
  5 #define dou double
  6 #define pb emplace_back
  7 #define mp make_pair
  8 #define sot(a,b) sort(a+1,a+1+b)
  9 #define rep1(i,a,b) for(int i=a;i<=b;++i)
 10 #define rep0(i,a,b) for(int i=a;i<b;++i)
 11 #define eps 1e-8
 12 #define int_inf 0x3f3f3f3f
 13 #define ll_inf 0x7f7f7f7f7f7f7f7f
 14 #define lson (curpos<<1)
 15 #define rson (curpos<<1|1)
 16 /* namespace */
 17 using namespace std;
 18 /* header end */
 19 
 20 const int maxn = 1e5 + 10;
 21 struct LinerBase {
 22     int cnt;
 23     ll b[32];
 24     void init() {
 25         cnt = 0;
 26         memset(b, 0, sizeof(b));
 27     }
 28     int insert(ll x) {
 29         for (int i = 31; i >= 0; i--)
 30             if (x & (1ll << i)) {
 31                 if (b[i]) x ^= b[i];
 32                 else {
 33                     b[i] = x;
 34                     cnt++;
 35                     return 1;
 36                 }
 37             }
 38         return 0;
 39     }
 40     bool check(ll x) {
 41         for (int i = 31; i >= 0; i--)
 42             if (x & (1ll << i)) x ^= b[i];
 43         return x == 0;
 44     }
 45 };
 46 
 47 LinerBase merge(LinerBase &a, LinerBase &b) {
 48     LinerBase c, d, all;
 49     for (int i = 31; i >= 0; i--) {
 50         c.b[i] = 0;
 51         all.b[i] = a.b[i];
 52         d.b[i] = 1ll << i;
 53     }
 54     for (int i = 31; i >= 0; i--)
 55         if (b.b[i]) {
 56             ll v = b.b[i], k = 0; int jud = 1;
 57             for (int j = 31; j >= 0; j--)
 58                 if ((1ll << j) & v)
 59                     if (all.b[j]) {
 60                         v ^= all.b[j];
 61                         k ^= d.b[j];
 62                     } else {
 63                         jud = 0;
 64                         all.b[j] = v;
 65                         d.b[j] = k;
 66                         break;
 67                     }
 68             if (jud) {
 69                 ll cur = 0;
 70                 for (int j = 31; j >= 0; j--)
 71                     if (k & (1ll << j)) cur ^= a.b[j];
 72                 c.insert(cur);
 73             }
 74         }
 75     return c;
 76 }
 77 LinerBase segt[maxn << 2], a[maxn];
 78 
 79 void maintain(int curpos) {
 80     segt[curpos] = merge(segt[lson], segt[rson]);
 81 }
 82 
 83 void build(int curpos, int curl, int curr) {
 84     if (curl == curr) {
 85         segt[curpos] = a[curl];
 86         return;
 87     }
 88     int mid = curl + curr >> 1;
 89     build(lson, curl, mid); build(rson, mid + 1, curr);
 90     maintain(curpos);
 91 }
 92 
 93 bool query(int curpos, ll x, int curl, int curr, int ql, int qr) {
 94     if (ql <= curl && curr <= qr)
 95         return segt[curpos].check(x);
 96     int mid = curl + curr >> 1;
 97     if (qr <= mid) return query(lson, x, curl, mid, ql, qr);
 98     if (ql > mid) return query(rson, x, mid + 1, curr, ql, qr);
 99     return query(lson, x, curl, mid, ql, qr) && query(rson, x, mid + 1, curr, ql, qr);
100 }
101 
102 int n, m;
103 
104 int main() {
105     scanf("%d%d", &n, &m);
106     rep1(i, 1, n) {
107         int _size; scanf("%d", &_size);
108         rep1(j, 1, _size) {
109             ll x; scanf("%lld", &x);
110             a[i].insert(x);
111         }
112     }
113     build(1, 1, n);
114     while (m--) {
115         int l, r; ll x; scanf("%d%d%lld", &l, &r, &x);
116         if (query(1, x, 1, n, l, r)) puts("YES");
117         else puts("NO");
118     }
119     return 0;
120 }
View Code

C:

这题是2018年南昌网络赛原题(几乎,用同样做法可过),题目链接:https://nanti.jisuanke.com/t/38228

枚举区间的复杂度已经是O(n^2),完全无法接受。但不同区间的最小值情况最多只有n种。而且对于一个最小值a[i],一定存在l,r使得a[i]*sum(bl..r)最大。

那么对于任意一个a[i],假设其为最小值,往左往右扩展确定最大的区间位置;然后分两种情况:

  1. 若a[i]>=0,那么显然要使得sum[r]-sum[l-1]最大。因为不知道具体会确定哪个区间,那么可以把最终的区间[l,r]分为[l,i]和[i,r]两个一端确定的区间,就变成查询max(b[i]-b[l])+max(b[r]-b[i])。由于线段树只能返回值而不能返回在b数组中的位置,所以可以建立两个数组分别记录b数组的前缀后缀和,然后在这两个数组上建线段树即可。
  2. 若a[i]<0,把上面的max改成min,同样做法。
 1 /* basic header */
 2 #include <bits/stdc++.h>
 3 /* define */
 4 #define ll long long
 5 #define dou double
 6 #define pb emplace_back
 7 #define mp make_pair
 8 #define sot(a,b) sort(a+1,a+1+b)
 9 #define rep1(i,a,b) for(int i=a;i<=b;++i)
10 #define rep0(i,a,b) for(int i=a;i<b;++i)
11 #define eps 1e-8
12 #define int_inf 0x3f3f3f3f
13 #define ll_inf 0x7f7f7f7f7f7f7f7f
14 #define lson (curpos<<1)
15 #define rson (curpos<<1|1)
16 /* namespace */
17 using namespace std;
18 /* header end */
19 
20 const int maxn = 3e6 + 10;
21 ll a[maxn], b[maxn], s1[maxn], s2[maxn], ans = -1e18;
22 int n, l[maxn], r[maxn];
23 
24 struct SegT {
25     struct Node {
26         ll maxx, minn;
27     } node[maxn << 2];
28 
29     void build(ll *s, int curpos, int curl, int curr) {
30         if (curl == curr) {
31             node[curpos].maxx = node[curpos].minn = s[curl];
32             return;
33         }
34         int mid = curl + curr >> 1;
35         build(s, lson, curl, mid); build(s, rson, mid + 1, curr);
36         node[curpos].maxx = max(node[lson].maxx, node[rson].maxx);
37         node[curpos].minn = min(node[lson].minn, node[rson].minn);
38     }
39 
40     ll queryMax(int curpos, int curl, int curr, int ql, int qr) {
41         if (ql <= curl && curr <= qr) return node[curpos].maxx;
42         int mid = curl + curr >> 1;
43         if (qr <= mid) return queryMax(lson, curl, mid, ql, qr);
44         if (ql > mid) return queryMax(rson, mid + 1, curr, ql, qr);
45         return max(queryMax(lson, curl, mid, ql, mid), queryMax(rson, mid + 1, curr, mid + 1, qr));
46     }
47 
48     ll queryMin(int curpos, int curl, int curr, int ql, int qr) {
49         if (ql <= curl && curr <= qr) return node[curpos].minn;
50         int mid = curl + curr >> 1;
51         if (qr <= mid) return queryMin(lson, curl, mid, ql, qr);
52         if (ql > mid) return queryMin(rson, mid + 1, curr, ql, qr);
53         return min(queryMin(lson, curl, mid, ql, mid), queryMin(rson, mid + 1, curr, mid + 1, qr));
54     }
55 } segt1, segt2;
56 
57 void determinLR() {
58     stack<int>st; st.push(1);
59     l[1] = 1; r[n] = n;
60     rep1(i, 2, n) {
61         while (!st.empty() && a[i] <= a[st.top()]) st.pop();
62         if (st.empty()) l[i] = 1; else l[i] = st.top() + 1;
63         st.push(i);
64     }
65     while (!st.empty()) st.pop();
66     st.push(n);
67     for (int i = n - 1; i >= 1; i--) {
68         while (!st.empty() && a[i] <= a[st.top()]) st.pop();
69         if (st.empty()) r[i] = n; else r[i] = st.top() - 1;
70         st.push(i);
71     }
72 }
73 
74 int main() {
75     scanf("%d", &n);
76     s1[0] = s2[n + 1] = 0;
77     rep1(i, 1, n) scanf("%lld", &a[i]);
78     rep1(i, 1, n) {
79         scanf("%lld", &b[i]);
80         s1[i] = s1[i - 1] + b[i];
81     }
82     for (int i = n; i >= 1; i--) s2[i] = s2[i + 1] + b[i];
83     segt1.build(s1, 1, 1, n); segt2.build(s2, 1, 1, n);
84     determinLR();
85     rep1(i, 1, n) {
86         ll lsum = segt2.queryMax(1, 1, n, l[i], i) - s2[i + 1], rsum = segt1.queryMax(1, 1, n, i, r[i]) - s1[i - 1];
87         ll tmp = a[i] * (lsum + rsum - b[i]);
88         ans = max(tmp, ans);
89         lsum = segt2.queryMin(1, 1, n, l[i], i) - s2[i + 1], rsum = segt1.queryMin(1, 1, n, i, r[i]) - s1[i - 1];
90         tmp = a[i] * (lsum + rsum - b[i]);
91         ans = max(ans, tmp);
92     }
93     printf("%lld
", ans);
94     return 0;
95 }
View Code

D:

当时打了个表看了几分钟,突然有种感觉:对于有解的不能被3整除的数n,一定只用两个数就能或出来吗?

答案是显然的。分类讨论n%3的结果即可。

  1 /* basic header */
  2 #include <bits/stdc++.h>
  3 /* define */
  4 #define ll long long
  5 #define dou double
  6 #define pb emplace_back
  7 #define mp make_pair
  8 #define sot(a,b) sort(a+1,a+1+b)
  9 #define rep1(i,a,b) for(int i=a;i<=b;++i)
 10 #define rep0(i,a,b) for(int i=a;i<b;++i)
 11 #define eps 1e-8
 12 #define int_inf 0x3f3f3f3f
 13 #define ll_inf 0x7f7f7f7f7f7f7f7f
 14 #define lson (curpos<<1)
 15 #define rson (curpos<<1|1)
 16 /* namespace */
 17 using namespace std;
 18 /* header end */
 19 
 20 const int maxn = 1e2 + 10;
 21 int func[maxn];
 22 
 23 int main() {
 24     int t; scanf("%d", &t);
 25     while (t--) {
 26         ll n; scanf("%lld", &n);
 27         if (n % 3 == 0) {
 28             printf("1 %lld
", n);
 29             continue;
 30         }
 31         ll cmp = n;
 32         int cnt = 0;
 33         while (cmp) {
 34             func[++cnt] = cmp & 1;
 35             cmp >>= 1;
 36         }
 37         rep0(i, 2, cnt) {
 38             ll ans1 = 0, ans2 = 0;
 39             for (int j = cnt; j >= i + 1; j--) ans1 = ans1 * 2 + func[j];
 40             for (int j = i; j >= 1; j--) ans2 = ans2 * 2 + func[j];
 41             rep1(j, 1, i) ans1 *= 2;
 42             if (ans2 % 3 == 1) {
 43                 rep1(j, i + 1, cnt)
 44                 if (j % 2 == 0 && func[j]) {
 45                     ans2 |= (1ll << j - 1);
 46                     break;
 47                 }
 48                 int flag = 0;
 49                 if (ans2 % 3 != 0)
 50                     rep1(j, i + 1, cnt)
 51                     if (j % 2 == 1 && func[j]) {
 52                         if (!flag)flag = j;
 53                         else {
 54                             ans2 |= (1ll << flag - 1); ans2 |= (1ll << j - 1);
 55                             break;
 56                         }
 57                     }
 58             } else if (ans2 % 3 == 2) {
 59                 rep1(j, i + 1, cnt)
 60                 if (j % 2 == 1 && func[j]) {
 61                     ans2 |= (1ll << j - 1);
 62                     break;
 63                 }
 64                 int flag = 0;
 65                 if (ans2 % 3 != 0)
 66                     rep1(j, i + 1, cnt)
 67                     if (j % 2 == 0 && func[j]) {
 68                         if (!flag)flag = j;
 69                         else {
 70                             ans2 |= (1ll << flag - 1); ans2 |= (1ll << j - 1);
 71                             break;
 72                         }
 73                     }
 74             }
 75             if (ans2 % 3 != 0)continue;
 76             if (ans1 % 3 == 1) {
 77                 rep1(j, 1, i)
 78                 if (j % 2 == 0 && func[j]) {
 79                     ans1 |= (1ll << j - 1);
 80                     break;
 81                 }
 82                 int flag = 0;
 83                 if (ans2 % 3 != 0)
 84                     rep1(j, 1, i)
 85                     if (j % 2 == 1 && func[j]) {
 86                         if (!flag) flag = j;
 87                         else {
 88                             ans1 |= (1ll << flag - 1); ans1 |= (1ll << j - 1);
 89                             break;
 90                         }
 91                     }
 92             } else if (ans1 % 3 == 2) {
 93                 rep1(j, 1, i)
 94                 if (j % 2 == 1 && func[j]) {
 95                     ans1 |= (1ll << j - 1);
 96                     break;
 97                 }
 98                 int flag = 0;
 99                 if (ans1 % 3 != 0)
100                     rep1(j, 1, i)
101                     if (j % 2 == 0 && func[j]) {
102                         if (!flag) flag = j;
103                         else {
104                             ans1 |= (1ll << flag - 1); ans1 |= (1ll << j - 1);
105                             break;
106                         }
107                     }
108             }
109             if (ans1 % 3 != 0)continue; if ((ans1 | ans2) != n)continue;
110             printf("2 %lld %lld
", ans1, ans2);
111             break;
112         }
113     }
114 }
View Code

E:

dp。

F:

线段树+平衡树。

G:

树型dp。

H:

高斯消元数学题。

I:

字符串题。广义后缀自动机。

J:

dp+最短路。dp[i][j]表示从起点走到编号为i的点,至多免费走j条边的花费的最小值。

dp[i][j]=min(dp[i][j-1],min(dp[x][j-1],dp[x][j]+e)) 其中点x和点i有边权为e的边。

从小到大枚举j进行转移,以min(dp[i][j-1],dp[x][j-1])作为点i距离的初始值。时间复杂度O(kmlogn)。

 1 /* basic header */
 2 #include <bits/stdc++.h>
 3 /* define */
 4 #define ll long long
 5 #define dou double
 6 #define pb emplace_back
 7 #define mp make_pair
 8 #define sot(a,b) sort(a+1,a+1+b)
 9 #define rep1(i,a,b) for(int i=a;i<=b;++i)
10 #define rep0(i,a,b) for(int i=a;i<b;++i)
11 #define eps 1e-8
12 #define int_inf 0x3f3f3f3f
13 #define ll_inf 0x7f7f7f7f7f7f7f7f
14 #define lson (curpos<<1)
15 #define rson (curpos<<1|1)
16 /* namespace */
17 using namespace std;
18 /* header end */
19 
20 const int maxn = 1e3 + 10;
21 struct Edge {
22     int u, v, w;
23 } edge[maxn << 1];
24 int head[maxn], nxt[maxn << 1], tot = 0, dp[maxn][maxn];
25 
26 void init() {
27     tot = 0;
28     rep0(i, 0, maxn) head[i] = 0;
29     rep0(i, 0, maxn * 2) nxt[i] = 0;
30     rep0(i, 0, maxn) rep0(j, 0, maxn) dp[i][j] = int_inf;
31 }
32 
33 void addEdge(int u, int v, int w) {
34     nxt[++tot] = head[u];
35     head[u] = tot;
36     edge[tot].u = u;
37     edge[tot].v = v;
38     edge[tot].w = w;
39 }
40 
41 struct Node {
42     int x, d, k;
43     Node() {}
44     Node(int a, int b, int c): x(a), d(b), k(c) {}
45     bool operator<(const Node &rhs)const {
46         return d > rhs.d;
47     }
48 };
49 
50 int dijkstra(int s, int t, int k) {
51     priority_queue<Node, vector<Node>>q;
52     dp[s][k] = 0;
53     q.push(Node(s, 0, k));
54     while (!q.empty()) {
55         Node curr = q.top();
56         q.pop();
57         int u = curr.x, d = curr.d, k = curr.k;
58         if (d > dp[u][k]) continue;
59         if (u == t) return d;
60         for (int i = head[u]; i; i = nxt[i]) {
61             int v = edge[i].v, w = edge[i].w;
62             if (k && dp[v][k - 1] > d) {
63                 dp[v][k - 1] = d;
64                 q.push(Node(v, d, k - 1));
65             }
66             if (dp[v][k] > d + w) {
67                 dp[v][k] = d + w;
68                 q.push(Node(v, d + w, k));
69             }
70         }
71     }
72 }
73 
74 int main() {
75     init();
76     int n, m, s, t, k; scanf("%d%d%d%d%d", &n, &m, &s, &t, &k);
77     rep0(i, 0, m) {
78         int u, v, w; scanf("%d%d%d", &u, &v, &w);
79         if (u == v) continue;
80         addEdge(u, v, w); addEdge(v, u, w);
81     }
82     printf("%d
", dijkstra(s, t, k));
83     return 0;
84 }
View Code

K:

给定一个数字序列,问:该数字序列有多少个子序列,其值能被300整除。

注意到300的整数既能被3整除又能被100整除。对于被3整除,用一个数组记录前缀和%3的结果即可;对于被100整除,判断最后两位是否都为0。

 1 /* basic header */
 2 #include <bits/stdc++.h>
 3 /* define */
 4 #define ll long long
 5 #define dou double
 6 #define pb emplace_back
 7 #define mp make_pair
 8 #define sot(a,b) sort(a+1,a+1+b)
 9 #define rep1(i,a,b) for(int i=a;i<=b;++i)
10 #define rep0(i,a,b) for(int i=a;i<b;++i)
11 #define eps 1e-8
12 #define int_inf 0x3f3f3f3f
13 #define ll_inf 0x7f7f7f7f7f7f7f7f
14 #define lson (curpos<<1)
15 #define rson (curpos<<1|1)
16 /* namespace */
17 using namespace std;
18 /* header end */
19 
20 // const int maxn = 1e5 + 10;
21 const int maxn = 10;
22 char s[maxn];
23 int n, pre[maxn], cnt[3];
24 
25 int main() {
26     scanf("%s", s + 1); n = strlen(s + 1);
27     rep1(i, 1, n) pre[i] = (pre[i - 1] + s[i] - '0') % 3;
28     ll ans = 0;
29     rep1(i, 1, n) {
30         if (s[i] == '0') {
31             ans++; // 算单独0
32             if (s[i - 1] == '0') ans += cnt[pre[i - 1]]; // 如果前一位也是0,满足被100整除的要求
33         }
34         cnt[pre[i - 1]]++;
35     }
36     printf("%lld
", ans);
37     return 0;
38 }
View Code
原文地址:https://www.cnblogs.com/JHSeng/p/11257269.html