nowcoder 2020 多校 第五場
阿新 • • 發佈:2020-08-07
nowcoder 2020 多校 第五場
B Graph
tire樹,最小生成樹
題意:
給一個帶有邊權的樹,可以刪除或新增邊,但要保證:
- 圖聯通
- 環上邊權異或為0
Solution
不管怎麼操作,兩點路徑上邊權的異或值是固定的,於是問題就轉化成一個最小生成樹問題。每次選出不聯通的點集\(S_1\), \(S_2\),
將他們聯通的代價是
\[ \min\limits_{u\in S_1 v\in S_2} \{\mathord{dis}(u,v)\} \]
可以通過tire樹實現這個過程,就是tire樹合併子節點,複雜度為\(O(n\log n)\)
#include <cstdio> #include <stack> #include <set> #include <cmath> #include <map> #include <time.h> #include <vector> #include <iostream> #include <string> #include <cstring> #include <algorithm> #include <memory.h> #include <cstdlib> #include <queue> #include <iomanip> #include <bitset> #include <unordered_map> #include <assert.h> #define P pair<int, int> #define LL long long #define LD long double #define PLL pair<LL, LL> #define mset(a, b) memset(a, b, sizeof(a)) #define rep(i, a, b) for (int i = a; i < b; i++) #define PI acos(-1.0) #define random(x) rand() % x #define debug(x) cout << #x << " " << x << "\n" using namespace std; const int inf = 0x3f3f3f3f; const LL __64inf = 0x3f3f3f3f3f3f3f3f; #ifdef DEBUG const int MAX = 1e3 + 50; #else const int MAX = 3e6 + 50; #endif const int mod = 10007; LL pow2[100]; vector<P> G[MAX]; int dis[MAX]; struct Tire{ int nex[MAX][2]; int siz[MAX]; int tot ; LL ans; void init(){ ans = 0; siz[0] = 0; mset(nex[0], 0); tot = 1; } void add(int x, int d) { int rt = 0; for(int i = d; i >= 0; i--){ int e = x & pow2[i] ? 1 : 0; if(!nex[rt][e]) { mset(nex[tot], 0); siz[tot] = 0; nex[rt][e] = tot ++; } rt = nex[rt][e]; siz[rt] ++; } } LL query(int val, int u, int d) { int rt = u; LL res = val; for(int i = d; i >= 0; i--){ if(res & pow2[i]) { if(nex[rt][1]) { res ^= pow2[i]; rt = nex[rt][1]; } else rt = nex[rt][0]; } else { if(nex[rt][0]) { rt = nex[rt][0]; } else { res ^= pow2[i]; rt = nex[rt][1]; } } } return res; } LL merge(int l, int r, int val, int d, int row_d){ if(!nex[l][0] and !nex[l][1]) { return query(val, r, row_d); } LL res = inf; if(nex[l][0]) res = min(res, merge(nex[l][0], r, val, d-1, row_d)); if(nex[l][1]) res = min(res, merge(nex[l][1], r, val^pow2[d], d-1, row_d)); return res; } void dfs(int u, int val, int d) { if(nex[u][0]) dfs(nex[u][0], val, d-1); if(nex[u][1]) dfs(nex[u][1], val ^ pow2[d], d-1); if(!nex[u][0] or !nex[u][1]) return; int l = nex[u][0], r = nex[u][1]; if(siz[l] > siz[r]) swap(l, r); ans += 1LL * merge(l, r, 0, d-1, d-1) + pow2[d]; } }tire; void dfs(int u, int p, int d){ dis[u] = d; for(int i = 0; i < G[u].size(); i++){ int v = G[u][i].first; if(v == p) continue; dfs(v, u, d^G[u][i].second); } } int main(){ #ifdef DEBUG freopen64("in", "r", stdin); #endif pow2[0] = 1; for(int i = 1; i < 32; i++) pow2[i] = pow2[i-1] << 1; int n; scanf("%d", &n); for(int i = 1; i < n; i++) { int u, v, c; scanf("%d%d%d", &u, &v, &c); u++, v++; G[u].emplace_back(v, c); G[v].emplace_back(u, c); } dfs(1, -1, 0); tire.init(); for(int i = 1; i <= n; i++) tire.add(dis[i], 29); tire.dfs(0, 0, 29); printf("%lld\n", tire.ans); return 0; }
D Drop Voicing
最長上升子序列
題意
給一個序列\(p_1\dots p_n\),可以進行兩種操作,進行連續的第一個操作耗費一個代價
- 把\(p_{n-1}\)放到最前面
- \(p_1\)放到最後面
求把序列變成升序的最小代價
Solution
結合兩種操作其實就是把一個元素\(p_i\)移到一個位置,需要一個代價。於是變成求LIS
int n; int a[MAX]; int b[MAX]; int solve(){ mset(b, 0); int ans = 0; for(int i = 1; i <= n; i++) { int idx = lower_bound(b, b+ans, a[i]) - b; b[idx] = a[i]; if(idx == ans) ans ++; } // cout << n - ans << "\n"; return ans; } int main(){ #ifdef DEBUG freopen64("in", "r", stdin); #else ios::sync_with_stdio(0); cin.tie(0); #endif cin >> n; for(int i = 1; i <= n; i++) cin >> a[i]; int ans = 0; for(int i = 0; i < n; i++) { rotate(a+1, a+2, a+n+1); ans = max(ans, solve()); } cout << n - ans << "\n"; return 0; }
E Bogo Sort
from math import gcd
def lcm(a:int, b:int) ->int:
return a * b // gcd(a, b)
n = int(input())
a = [0] + list(map(int, input().split()))
vis = [False for i in range(len(a))]
ans = 1
for i in range(1, n+1):
x = a[i]
if vis[x] : continue
cur = 0
while not vis[x] :
vis[x] = True
cur += 1
x = a[x]
ans = lcm(ans, cur)
print(ans)