第二十屆西南科技大學ACM程式設計競賽(同步賽)

Rosmontis_L發表於2024-06-09

第二十屆西南科技大學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......

相關文章