牛客小白月赛11题解简录
A.官方说是随机数据所以暴力线段树……
#include <bits/stdc++.h> using namespace std; const int maxn = 1e5 + 5; int N, L; class SegmentTree { public: #define ls(p) p << 1 #define rs(p) p << 1 | 1 struct Node { int l, r, minn, tag; }t[maxn << 2]; void Push_up(int p) { t[p].minn = min(t[ls(p)].minn, t[rs(p)].minn); } void Push_down(int p) { if (t[p].tag) { t[ls(p)].minn += t[p].tag; t[rs(p)].minn += t[p].tag; t[ls(p)].tag += t[p].tag; t[rs(p)].tag += t[p].tag; t[p].tag = 0; } } void Build(int l, int r, int p) { t[p].l = l, t[p].r = r; if (l == r) { t[p].minn = t[p].tag = 0; return; } int mid = (l + r) >> 1; Build(l, mid, ls(p)); Build(mid + 1, r, rs(p)); } void Modify(int l, int r, int p, int k) { if (l <= t[p].l && t[p].r <= r) { t[p].minn += k; t[p].tag += k; return; } Push_down(p); int mid = (t[p].l + t[p].r) >> 1; if (l <= mid) Modify(l, r, ls(p), k); if (mid < r) Modify(l, r, rs(p), k); Push_up(p); } int Query(int l, int r, int p) { if (l <= t[p].l && t[p].r <= r && t[p].minn > 0) return t[p].r - t[p].l + 1; if (t[p].l == t[p].r) return t[p].minn > 0; Push_down(p); int mid = (t[p].l + t[p].r) >> 1; if (l > mid) return Query(l, r, rs(p)); if (r <= mid) return Query(l, r, ls(p)); return Query(l, r, ls(p)) + Query(l, r, rs(p)); } }T; map<pair<int, int>, int> mp; int main() { scanf("%d %d", &N, &L); T.Build(1, L + 1, 1); for (int i = 0; i < N; i++) { int op, x, y; scanf("%d %d %d", &op, &x, &y); x++, y++; if (x > y) swap(x, y); if (op == 1 && !mp[make_pair(x, y)]) { T.Modify(x, y, 1, 1); mp[make_pair(x, y)]++; } else if (op == 2 && mp[make_pair(x, y)]) { T.Modify(x, y, 1, -1); mp[make_pair(x, y)]--; } else if (op == 3) { cout << T.Query(1, L + 1, 1) << endl; } } return 0; }
B.方法是拆点,把每个点都拆成k+1个,表示已经使用过k(k属于0~K)个戒严到达此点。连边很关键。
1.连有向边,如果u本身是戒严的,则枚举u + k * n,连向v + (k + 1) * n,意义是到u用了k个,则想到达v就得用k + 1个了。否则u + k * n连向v + k * n即可,意义为不增加使用次数。而且如果u是戒严的,则k == K的个点根本不用连了,不可能再前进一步。从v向u连的也是一样的。
2.设立虚的起始点s和终点t,s只要和点1连接即可,因为s到点1(等价类)路过的戒严点不可能比0大。这也意味着其实我们连了这么多条边,大部分边其实都是与主图剥离的,也就是真正跑的时候根本没机会用到。而终点t就不同了,由于无法人为判定来“剪枝”,所以0~K都要与t相连。
3.虽然边多了点,dijkstra会被卡常可还行?不是说命题人会卡spfa吗……
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 typedef long long ll; 5 const ll INF = 2e18; 6 const int N = 805, M = 4005; 7 8 int n, m, k, hold[N], tot, s, t; 9 ll dis[N * 11]; 10 bool vis[N * 11]; 11 int head[N * 11]; 12 struct Edge { 13 int to, nxt; 14 ll cost; 15 }e[M * 22]; 16 17 void Add(int u, int v, ll cost) { 18 e[++tot].to = v, e[tot].nxt = head[u], e[tot].cost = cost, head[u] = tot; 19 } 20 21 void Get_Graph() { 22 for (int i = 1; i <= m; i++) { 23 int u, v; 24 ll cost; 25 scanf("%d %d %lld", &u, &v, &cost); 26 if (!hold[u]) { 27 for (int j = 0; j <= k; ++j) { 28 Add(u + j * n, v + j * n, cost); 29 } 30 } 31 if (!hold[v]) { 32 for (int j = 0; j <= k; ++j) { 33 Add(v + j * n, u + j * n, cost); 34 } 35 } 36 if (hold[u]) { 37 for (int j = 0; j < k; ++j) { 38 Add(u + j * n, v + (j + 1) * n, cost); 39 } 40 } 41 if (hold[v]) { 42 for (int j = 0; j < k; ++j) { 43 Add(v + j * n, u + (j + 1) * n, cost); 44 } 45 } 46 } 47 48 s = 0, t = n * (k + 1) + 1; 49 Add(s, 1, 0); 50 for (int i = 0; i <= k; i++) { 51 Add(n + i * n, t, 0); 52 } 53 } 54 55 ll SPFA() { 56 for (int i = s; i <= t; i++) 57 dis[i] = INF, vis[i] = false; 58 dis[s] = 0; 59 vis[s] = true; 60 61 queue<int> Q; 62 Q.push(s); 63 64 while (Q.size()) { 65 int x = Q.front(); Q.pop(); 66 vis[x] = false; 67 68 for (int i = head[x]; i; i = e[i].nxt) { 69 int to = e[i].to; 70 if (dis[to] > dis[x] + e[i].cost) { 71 dis[to] = dis[x] + e[i].cost; 72 if (!vis[to]) Q.push(to); 73 } 74 } 75 } 76 77 return dis[t]; 78 } 79 80 int main() { 81 scanf("%d %d %d", &n, &m, &k); 82 for (int i = 1; i <= n; i++) 83 scanf("%d", &hold[i]); 84 Get_Graph(); 85 86 ll ans = SPFA(); 87 printf("%lld\n", ans == INF ? -1 : ans); 88 89 return 0; 90 }
C.签到。模拟一下即可。话说我为啥要开个map强行提高复杂度……
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 int n, m, T; 5 map<int, int> row, column; 6 7 int main() { 8 scanf("%d %d %d", &n, &m, &T); 9 vector<vector<int>> v(n, vector<int>(m, 0)); 10 11 for (int i = 1; i <= T; ++i) { 12 int op, x; 13 scanf("%d %d", &op, &x); 14 x--; 15 if (op == 1) row[x] = i; 16 else column[x] = i; 17 } 18 19 for (int i = 0; i < n; i++) 20 for (int j = 0; j < m; j++) { 21 if (row[i]) v[i][j] = max(v[i][j], row[i]); 22 if (column[j]) v[i][j] = max(v[i][j], column[j]); 23 printf("%d%c", v[i][j], " \n"[j == m - 1]); 24 } 25 26 return 0; 27 }
D.正常地二叉搜索树去做会被有序卡成n方。考虑记录id后的数字排序,可以像中序遍历一样,先找到[l, r]中最早出现的那个,它的ans就是当前深度,然后两边进行分治解决。但如果暴力扫一遍去寻找最早出现的,还是n方的,所以用ST表预处理一下区间最小值。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int maxn = 3e5 + 5; 5 int N; 6 long long Ans[maxn]; 7 struct V { 8 int val, id; 9 bool operator < (const V &rhs) const { 10 return val < rhs.val; 11 } 12 }a[maxn]; 13 int ST[maxn][20], pos[maxn]; 14 15 void Pre() { 16 for (int i = 1; i <= N; i++) 17 ST[i][0] = a[i].id; 18 for (int j = 1; (1 << j) <= N; j++) { 19 for (int i = 1; i + (1 << j) - 1 <= N; i++) { 20 ST[i][j] = min(ST[i][j - 1], ST[i + (1 << (j-1))][j - 1]); 21 } 22 } 23 for (int i = 1; i <= N; i++) 24 pos[a[i].id] = i; 25 } 26 27 int Query(int l, int r) { 28 int j = log2(r - l + 1); 29 return min(ST[l][j], ST[r - (1 << j) + 1][j]); 30 } 31 32 void Divide(int l, int r, int depth) { 33 if (l > r) return; 34 35 int id = Query(l, r); 36 Ans[id] = depth; 37 Divide(l, pos[id] - 1, depth + 1); 38 Divide(pos[id] + 1, r, depth + 1); 39 } 40 41 int main() { 42 scanf("%d", &N); 43 for (int i = 1; i <= N; ++i) { 44 scanf("%d", &a[i].val); 45 a[i].id = i; 46 } 47 sort(a + 1, a + 1 + N); 48 Pre(); 49 Divide(1, N, 0); 50 for (int i = 1; i <= N; ++i) { 51 Ans[i] += Ans[i - 1]; 52 printf("%lld\n", Ans[i]); 53 } 54 return 0; 55 }
E.命题人本意是个裸的01分数规划,我读错题了,以为默认1是起点,其实是谁做起点都行只要有环路即可。因此就是二分里面套个判负环存在即可。
1 #include <bits/stdc++.h> 2 #define fi first 3 #define se second 4 using namespace std; 5 6 typedef double db; 7 const int maxn = 2005; 8 const db eps = 1e-4; 9 int n, m, vis[maxn]; 10 vector<pair<int, db>> adj[maxn]; 11 db dis[maxn]; 12 13 bool dfs(int cur, db ans) { 14 vis[cur] = 1; 15 for (auto i : adj[cur]) { 16 if (dis[i.fi] > dis[cur] + i.se - ans) { 17 dis[i.fi] = dis[cur] + i.se - ans; 18 if (vis[i.fi] || dfs(i.fi, ans)) 19 return vis[cur] = 0, 1; 20 } 21 } 22 return vis[cur] = 0; 23 } 24 25 bool ok(db mid) { 26 memset(dis, 0, sizeof(dis)); 27 for (int i = 1; i <= n; i++) 28 if (dfs(i, mid)) 29 return 1; 30 return 0; 31 } 32 33 int main() { 34 scanf("%d %d", &n, &m); 35 for (int i = 0; i < m; i++) { 36 int u, v; db c; 37 scanf("%d %d %lf", &u, &v, &c); 38 adj[u].push_back({v, c}); 39 } 40 db l = 0, r = 1e9 + 5; 41 while (r - l > eps) { 42 db mid = (l + r) / 2.0; 43 if (ok(mid)) r = mid; 44 else l = mid; 45 } 46 if (l <= 1e9) printf("%.2lf\n", l); 47 else puts("Rinne is cute"); 48 }
F.树根和树叶不连通的最小代价,dfs并且dp处理一下,选min(cur-fa的cost, son的和)。
1 #include <bits/stdc++.h> 2 #define pb push_back 3 #define fi first 4 #define se second 5 using namespace std; 6 7 typedef long long ll; 8 typedef pair<int, ll> pil; 9 const int maxn = 1e5 + 5; 10 int N, M, S; 11 vector<pil> adj[maxn]; 12 ll f[maxn]; 13 14 void dfs(int cur, int fa) { 15 ll res = 0LL; 16 for (auto i : adj[cur]) { 17 if (i.fi == fa) { 18 if (cur != S) f[cur] = i.se; 19 continue; 20 } 21 dfs(i.fi, cur); 22 res += f[i.fi]; 23 } 24 if (cur == S || (adj[cur].size() > 1 && f[cur] > res)) f[cur] = res; 25 } 26 27 int main() { 28 scanf("%d %d %d", &N, &M, &S); 29 for (int i = 0; i < M; i++) { 30 int u, v; 31 ll cost; 32 scanf("%d %d %lld", &u, &v, &cost); 33 adj[u].pb({v, cost}); 34 adj[v].pb({u, cost}); 35 } 36 dfs(S, 0); 37 printf("%lld\n", f[S]); 38 return 0; 39 }
G.每读入一个数,二进制枚举其素因子(不超过7个)的组合,容斥修改答案即可。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int maxn = 2e5 + 5, maxa = 5e5 + 5; 5 int N, opt, x, ans; 6 int primes[maxa], v[maxa], tot; 7 int num[maxa]; 8 bool in[maxa]; 9 10 void pre() { 11 for (int i = 2; i < maxa; i++) { 12 if (!v[i]) { 13 primes[tot++] = i; 14 v[i] = i; 15 } 16 for (int j = 0; j < tot && (long long)primes[j] * i < maxa; j++) { 17 v[primes[j] * i] = primes[j]; 18 if (i % primes[j] == 0) break; 19 } 20 } 21 } 22 23 int main() { 24 pre(); 25 scanf("%d", &N); 26 while (N--) { 27 scanf("%d %d", &opt, &x); 28 if (opt == 1 && in[x]) continue; 29 if (opt == 2 && !in[x]) continue; 30 if (opt == 3) { 31 printf("%d\n", ans); 32 continue; 33 } 34 int que[10], tail = 0, t = x; 35 while (v[t]) { 36 int k = v[t]; 37 que[tail++] = k; 38 while (t % k == 0) t /= k; 39 } 40 for (int i = 0; i < (1 << tail); i++) { 41 int tmp = 1, cnt = 0; 42 for (int j = 0; j < tail; j++) { 43 if ((1 << j) & i) { 44 cnt++; 45 tmp *= que[j]; 46 } 47 } 48 if (opt == 1) { 49 ans += cnt & 1 ? -num[tmp] : num[tmp]; 50 num[tmp]++; 51 } else { 52 num[tmp]--; 53 ans += cnt & 1 ? num[tmp] : -num[tmp]; 54 } 55 } 56 in[x] ^= 1; 57 } 58 return 0; 59 }
G的方法二是式子稍微改一改弄成莫比乌斯函数的形式,然后枚举当前读入x的因数d,视为x是d的倍数,更新答案即可。factor不能预处理会被卡掉。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 const int maxn = 2e5 + 5, maxa = 5e5 + 5; 5 int N, opt, x, ans; 6 int primes[maxa], mu[maxa], tot; 7 int num[maxa]; 8 bool in[maxa], vis[maxa]; 9 10 void pre() { 11 mu[1] = 1; 12 for (int i = 2; i < maxa; i++) { 13 if (!vis[i]) { 14 primes[tot++] = i; 15 mu[i] = -1; 16 } 17 for (int j = 0; j < tot && (long long)primes[j] * i < maxa; j++) { 18 vis[primes[j] * i] = true; 19 if (i % primes[j] == 0) break; 20 mu[primes[j] * i] = -mu[i]; 21 } 22 } 23 } 24 25 void Update(int i, int opt) { 26 ans -= (long long)num[i] * (num[i] - 1) / 2 * mu[i]; 27 num[i] += opt == 1 ? 1 : -1; 28 ans += (long long)num[i] * (num[i] - 1) / 2 * mu[i]; 29 } 30 31 int main() { 32 pre(); 33 scanf("%d", &N); 34 while (N--) { 35 scanf("%d %d", &opt, &x); 36 if (opt == 1 && in[x]) continue; 37 if (opt == 2 && !in[x]) continue; 38 if (opt == 3) { 39 printf("%d\n", ans); 40 continue; 41 } 42 for (int i = 1; i * i <= x; i++) { 43 if (x % i == 0) { 44 Update(i, opt); 45 if (x / i != i) Update(x / i, opt); 46 } 47 } 48 in[x] ^= 1; 49 } 50 return 0; 51 }
H.边权只有三种情况所以拆点跑最短路即可。
1 #include <bits/stdc++.h> 2 #define pb push_back 3 #define fi first 4 #define se second 5 using namespace std; 6 7 typedef double db; 8 typedef pair<db, int> pdi; 9 const db INF = 1e18; 10 int N, M, st, ed; 11 vector<pdi> adj[300005]; 12 db dis[300005]; 13 14 db dij(int st, int ed) { 15 for (int i = st; i <= ed; i++) dis[i] = INF; 16 dis[st] = 0.0; 17 priority_queue<pdi, vector<pdi>, greater<pdi>> Q; 18 Q.push({0.0, st}); 19 while (Q.size()) { 20 auto x = Q.top(); Q.pop(); 21 if (dis[x.se] < x.fi) continue; 22 for (auto i : adj[x.se]) { 23 if (dis[i.se] > dis[x.se] + i.fi) { 24 dis[i.se] = dis[x.se] + i.fi; 25 Q.push({dis[i.se], i.se}); 26 } 27 } 28 } 29 return dis[ed]; 30 } 31 32 void add(int u, int v, db cost) { 33 adj[u].pb({fabs(cost), v + N}); 34 adj[u + N].pb({fabs(1.0 / (1.0 - cost)), v + N * 2}); 35 adj[u + N * 2].pb({fabs(1 - 1.0 / cost), v}); 36 } 37 38 int main() { 39 scanf("%d %d", &N, &M); 40 for (int i = 0; i < M; i++) { 41 int u, v; db cost; 42 scanf("%d %d %lf", &u, &v, &cost); 43 add(u, v, cost); add(v, u, cost); 44 } 45 st = 0, ed = N * 3 + 1; 46 adj[st].pb({0.0, 1}); 47 for (int i = 0; i < 3; i++) { 48 adj[N + N * i].pb({0.0, ed}); 49 } 50 51 db ans = dij(st, ed); 52 if (ans < INF) printf("%.3lf\n", ans); 53 else puts("-1"); 54 return 0; 55 }
I.第一步是把C的求法递推式改一改,是全部的A1~i、B1~i的异或和;第二步按位求贡献。
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 typedef long long ll; 5 const int mod = 1e9 + 7; 6 int n; 7 int cntA[2][32], cntB[2][32], Pow[32]; 8 9 int main() { 10 scanf("%d", &n); 11 vector<int> A(n), B(n); 12 for (int i = 0; i < 32; i++) { 13 Pow[i] = i ? 2LL * Pow[i - 1] % mod : 1; 14 } 15 for (int i = 0; i < n; i++) { 16 scanf("%d", &A[i]); 17 } 18 for (int i = 0; i < n; i++) { 19 scanf("%d", &B[i]); 20 } 21 for (int i = 0; i < n; i++) { 22 int a = A[i], b = B[i], tmp = 0; 23 for (int j = 0; j < 32; j++) { 24 cntA[a % 2][j]++; 25 cntB[b % 2][j]++; 26 a /= 2, b /= 2; 27 tmp = ((ll)tmp + (ll)Pow[j] * ((ll)cntA[0][j] * cntB[1][j] % mod + (ll)cntA[1][j] * cntB[0][j] % mod) % mod) % mod; 28 } 29 printf("%d ", tmp); 30 } 31 return 0; 32 }
J.签到。唯一分解一下或者暴力sqrt都可以
1 #include <bits/stdc++.h> 2 using namespace std; 3 4 int T, n, a, b; 5 6 void solve(int n) { 7 vector<int> factor, cnt; 8 for (int i = 2; i * i <= n; ++i) { 9 if (n % i == 0) { 10 factor.push_back(i); 11 cnt.push_back(0); 12 while (n % i == 0) { 13 n /= i; 14 cnt.back()++; 15 } 16 } 17 } 18 if (n > 1) factor.push_back(n), cnt.push_back(1); 19 20 a = 1, b = 1; 21 for (int i = 0; i < factor.size(); i++) { 22 int d = factor[i], p = cnt[i] / 2; 23 a *= pow(d, p); 24 if (cnt[i] & 1) b *= d; 25 } 26 } 27 28 int main() { 29 for (scanf("%d", &T); T; T--) { 30 scanf("%d", &n); 31 if (n < 0) puts("-1"); 32 else { 33 solve(n); 34 printf("%d %d\n", a, b); 35 } 36 } 37 return 0; 38 }