第二十屆西南科技大學ACM程式設計競賽(同步賽)
A:異或症
題意:給定一個排列,選任意i, j,使得 pi = pi ^ j,最後求字首異或陣列,求這個陣列的最大和
思路:發現可以把所有數變成出現過的二進位制位的和
void solve(){ ll n; cin >> n; map<ll, ll> mp; for(int i = 1; i <= n; i ++){ ll x; cin >> x; for(int i = 0; i <= 40; i ++){ if(x >> i & 1){ mp[1ll << i] = 1; } } } ll ans = 0; for(auto [x, y] : mp) ans += x; ans *= n; cout << ans << '\n'; }
B:拾取糖果
題意:直角座標平面上有 n 個糖果,糖果都處於整數點上,求任意一條透過原點的直線最多可以經過幾個糖果
思路:對座標除以 gcd 即可,注意特判一下在座標軸上和原點的糖果即可
void solve(){ ll n; cin >> n; map<PLL, ll> mp; ll ze = 0; for(int i = 1; i <= n; i ++){ ll x, y; cin >> x >> y; if(x == 0 && y == 0){ ze ++; continue; } else if(x == 0){ mp[{0, 1}] ++; continue; } else if(y == 0){ mp[{1, 0}] ++; continue; } ll gd = __gcd(x, y); x /= gd; y /= gd; mp[{x, y}] ++; } ll ans = 0; for(auto [x, y] : mp) ans = max(ans, y + ze); cout << ans << '\n'; }
C:圖騰
題意:長度為 n 的數軸上有 m 個圖騰,每個點的能量為左邊最近的圖騰的能量 * 右邊最近的圖騰的能量,q 次操作,插入圖騰或者求區間內點的能量值的和
思路:線段樹維護即可,倒過來看作每次刪除圖騰,預處理每個點的左邊的圖騰和右邊的圖騰,每次刪除圖騰就修改 pre + 1 ~ i - 1,i ~ i,i + 1 ~ nxt - 1,這三個區間即可,線段樹維護一下區間和
struct Node{ ll sum, siz; ll add; }; struct segtree{ ll n; vector<Node> a; segtree(ll _n) : n(_n * 4 + 10), a(n + 1) {} void pushup(ll u){ a[u].sum = a[u * 2].sum + a[u * 2 + 1].sum; } void eval(Node & t, ll add){ t.sum = t.sum + add * t.siz; t.add = t.add + add; } void pushdown(ll u){ eval(a[u * 2], a[u].add); eval(a[u * 2 + 1], a[u].add); a[u].add = 0; } void build(ll u, ll l, ll r, vector<ll> & arr){ if(l == r){ a[u] = {arr[l], 1, 0}; return; } ll mid = l + r >> 1ll; a[u] = {0, r - l + 1, 0}; build(u * 2, l, mid, arr); build(u * 2 + 1, mid + 1, r, arr); pushup(u); } void modify(ll u, ll l, ll r, ll l1, ll r1, ll add){ if(l1 <= l && r <= r1) eval(a[u], add); else{ pushdown(u); ll mid = l + r >> 1ll; if(l1 <= mid) modify(u * 2, l, mid, l1, r1, add); if(r1 > mid) modify(u * 2 + 1, mid + 1, r, l1, r1, add); pushup(u); } } ll query(ll u, ll l, ll r, ll l1, ll r1){ if(l1 <= l && r <= r1) return a[u].sum; else{ pushdown(u); ll mid = l + r >> 1ll; ll res = 0; if(l1 <= mid) res += query(u * 2, l, mid, l1, r1); if(r1 > mid) res += query(u * 2 + 1, mid + 1, r, l1, r1); return res; } } }; ll n, m, q; struct node{ ll op, l, r; }; void solve(){ cin >> n >> m >> q; vector<ll> p(n + 1); vector<ll> x(m + 1), v(m + 1); for(int i = 1; i <= m; i ++) cin >> x[i]; for(int i = 1; i <= m; i ++){ cin >> v[i]; p[x[i]] = v[i]; } vector<node> now(q + 1); for(int i = 1; i <= q; i ++){ ll op, l, r; cin >> op >> l >> r; now[i] = {op, l, r}; if(op == 1){ p[l] = r; } } vector<ll> ans; vector<ll> pre(n + 1), nxt(n + 1); vector<ll> he(n + 1); for(int i = 1, j = 0; i <= n; i ++){ if(j != 0) pre[i] = j; if(p[i] != 0) j = i; } for(int i = n, j = 0; i >= 1; i --){ if(j != 0) nxt[i] = j; if(p[i] == 0 && pre[i] && nxt[i]) he[i] = p[pre[i]] * p[nxt[i]]; if(p[i] != 0) j = i; } segtree seg(n);//初始化線段樹大小 seg.build(1, 1, n, he);//建樹,s是vector陣列 for(int i = q; i >= 1; i --){ auto [op, l, r] = now[i]; if(op == 1){ ll pp = pre[l], qq = nxt[l]; ll p1 = p[pp] * p[qq]; seg.modify(1, 1, n, pp + 1, l - 1, p1 - seg.query(1, 1, n, l - 1, l - 1)); seg.modify(1, 1, n, l + 1, qq - 1, p1 - seg.query(1, 1, n, l + 1, l + 1)); seg.modify(1, 1, n, l, l, p1); nxt[pp] = qq; pre[qq] = pp; } else{ ll res = seg.query(1, 1, n, l, r); ans.push_back(res); } } for(int i = ans.size() - 1; i >= 0; i --) cout << ans[i] << '\n'; }
D:能源
題意:n 個庇護所,每個庇護所需要 ai 的能源,有 m 個能提供 bi 能源的電池,求滿足所有庇護所需求的同時最小的電池能源之和
思路:排序雙指標即可
void solve(){ ll n, m; cin >> n >> m; vector<ll> a(n + 1), b(m + 1); for(int i = 1; i <= n; i ++) cin >> a[i]; for(int i = 1; i <= m; i ++) cin >> b[i]; sort(a.begin() + 1, a.end()); sort(b.begin() + 1, b.end()); ll ans = 0; int i = 1, j = 1; for(i = 1, j = 1; i <= n && j <= m; i ++){ while(j <= m && a[j] < a[i]) j ++; ans += a[j]; j ++; } cout << ans << '\n'; }
E:又雙叒叕分糖果
題意:兩個糖果包分別有一些重量的糖果,丟掉每個糖果包一半數量的糖果,然後再從兩個糖果剩餘的糖果中選取糖果組成新的糖果包,新的糖果包中不能有相同重量的糖果,求新的糖果包最多能有幾種不同重量的糖果
思路:先分別統計兩個糖果包的糖果種類,種類相同的數量全部丟掉,如果丟掉了重複的還不夠就從糖果種類中丟掉,求每個糖果包中能選幾種糖果,同時記錄一下兩個糖果包都有的糖果,優先選掉各自獨有的糖果種類,然後才從共有的糖果中選取
void solve(){ ll n; cin >> n; ll x, y; cin >> x >> y; ll sum1 = 0, sum2 = 0; map<ll, ll> a, b, c1; for(int i = 1; i <= x; i ++){ ll w, num; cin >> w >> num; sum1 += num - 1; a[w] ++; } for(int i = 1; i <= y; i ++){ ll w, num; cin >> w >> num; sum2 += num - 1; b[w] ++; if(a.count(w)) c1[w] ++; } ll ans = 0; ll cha1 = 0, cha2 = 0; if(sum1 < n / 2) cha1 = n / 2 - sum1; if(sum2 < n / 2) cha2 = n / 2 - sum2; x -= cha1, y -= cha2; for(auto [c, d] : a){ if(x <= 0) break; if(!c1.count(c)){ x --; ans ++; } } for(auto [c, d] : b){ if(y <= 0) break; if(!c1.count(c)){ y --; ans ++; } } ll yu = x + y; ll get = min(yu, (ll)(c1.size())); ans += get; cout << ans << '\n'; }
J:再聚端午,祝全體老師、同學、志願者們,端午安康!
題意:四個點,A-B-C-D-A,求總路程
思路:一個求距離函式,呼叫即可
double dist(double x1, double y1, double x2, double y2){ double ans = 0; ans = (x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2); ans = sqrt(ans); return ans; } void solve(){ vector<double> x(5), y(5); for(int i = 1; i <= 4; i ++){ cin >> x[i] >> y[i]; } double ans = 0; ans += dist(x[1], y[1], x[2], y[2]); ans += dist(x[2], y[2], x[3], y[3]); ans += dist(x[3], y[3], x[4], y[4]); ans += dist(x[4], y[4], x[1], y[1]); cout << fixed << setprecision(15) << ans; }
K:枝江一夢
題意:給定二維平面,初始在 X 正半軸有 n 點都在 y=0 處。有 m 次操作,每次操作會使得在 x=b 點提高 a×c 然後擴散其周圍,但是 c 遞減,直到 c=0。問最後正半軸 [1,n] 的 y 軸座標
思路:先記錄一下每個點到左邊的最遠能到的地方,分別在當前點和左邊界標一下,然後分別維護加的數量add_n,減的數量del_n,當前需要加的值add,當前需要減的值del,每次就把後兩項分別加上對應的前兩項,當到達的點是開始點,也就是初始操作的地方,再維護往後加的數量add_nf,往後減的數量add_nf,當前往後加的值add_f,往後減的值del_f,每次把後兩項減去對應的前兩項,同時小根堆維護一下每個操作失效的時間即可
void solve(){ ll n, m; cin >> n >> m; vector<TLL> v[n + 10]; for(int i = 1; i <= m; i ++){ ll a, b, c; cin >> a >> b >> c; ll l = b - c + 1, r = b + c - 1; l = max(1ll, l), r = min(n, r); v[l].push_back({a * c, b, 1}); v[b].push_back({a * c, b, 2}); } ll add = 0, del = 0; ll add_n = 0, del_n = 0; ll add_f = 0, del_f = 0; ll add_nf = 0, del_nf = 0; priority_queue<PLL, vector<PLL>, greater<PLL>> q; for(int i = 1; i <= n; i ++){ for(auto [x, y, z] : v[i]){ ll x1 = abs(x); if(z == 1){ if(x < 0){ del_n ++; del += x1 - (y - i); } else{ add_n ++; add += x1 - (y - i); } } else{ q.push({i + x1, x}); if(x < 0){ del_n --; del -= x1; del_nf ++; del_f += x1; } else{ add_n --; add -= x1; add_nf ++; add_f += x1; } } } ll res = 0; res = add - del + add_f - del_f; add += add_n; del += del_n; while(q.size() && q.top().first <= i){ if(q.top().second < 0) del_nf --; else add_nf --; q.pop(); } add_f -= add_nf; del_f -= del_nf; cout << res << ' '; } }
Pending......