歷屆第3題

2016年3月

線段覆蓋長度

提示
差值陣列

題解
用差值陣列維護每個點上有多少線段
再數有幾個點上有一以上的字串


C++範例代碼:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
int main(){
    ios::sync_with_stdio(0);cin.tie(0);
    ll C = 10000000;
    ll n;
    cin >> n;
    vector<ll> d(C,0);
    while (n--){
        ll l,r;
        cin >> l >> r;
        d[l]++;
        d[r]--;
    }
    ll cur = 0;
    ll ans = 0;
    for (ll i = 0;i<C;i++){
        cur += d[i];
        if(cur>0)ans++;
    }
    cout << ans << "\n";
}


Python範例代碼:

2016年10月

定時K彈

提示

題解
用rope模擬操作


C++範例代碼:

#include<bits/stdc++.h>
#include<ext/rope>
using namespace std;
using namespace __gnu_cxx;
using ll = long long;
int main(){
    ios::sync_with_stdio(0);cin.tie(0);
    ll n, m, k;
    cin >> n >> m >> k;
    rope<ll> r;
    for (int i = 1;i<=n;i++){
        r.push_back(i);
    }
    ll alive = n;
    ll cur = 0;
    while (k--){
        cur = (cur+m-1)%alive;
        r.erase(cur,1);
        alive--;
    }
    cur = cur%alive;
    cout << r[cur] << "\n";
}


Python範例代碼:

2017年3月

數字龍捲風
注:本題難度較不符合第三題應有的難度

提示

題解

C++範例代碼:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
struct pos{
    ll x,y;
    pos(){}
    pos(ll X,ll Y):x(X),y(Y){}
    ll& get(vector<vector<ll>> &v){
        return v[x][y];
    }
};
pos operator+(const pos &a, const pos &b){
    return pos(a.x+b.x,a.y+b.y);
}
int main(){
    ios::sync_with_stdio(0);cin.tie(0);
    pos fs[4] = {pos(0,-1),pos(-1,0),pos(0,1),pos(1,0)};
    ll n;
    ll f;
    cin >> n >> f;
    ll i = 2;
    ll c = i/2;
    pos cur = pos(n/2,n/2);
    function<bool(pos&)> valid = [&n](pos &p){
        return p.x>=0&&p.y>=0&&p.x<n&&p.y<n;
    };
    vector<vector<ll>> arr(n);
    for (int x = 0;x<n;x++){
        arr[x].resize(n);
        for (int y = 0;y<n;y++){
            cin >> arr[x][y];
        }
    }
    while (valid(cur)){
        cout << cur.get(arr);
        cur = cur + fs[f];
        c--;
        if (c==0){
            i++;
            c = i/2;
            f = (f+1)%4;
        }
    }
    cout << "\n";
}


Python範例代碼:

2017年10月

樹狀圖分析

提示
dfs

題解
先找出根結點(不是其他人的子節點的點)
再從根結點往下dfs


C++範例代碼:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
ll ans = 0;
vector<vector<ll>> child;
ll dfs(ll i){
    ll h = 0;
    for (ll t : child[i]){
        h = max(h,dfs(t-1)+1);
    }
    ans+=h;
    return h;
}
int main(){
    ios::sync_with_stdio(0);cin.tie(0);
    ll n;
    cin >> n;
    child.resize(n);
    vector<bool> root(n,true);
    for (int i = 0;i<n;i++){
        ll k;
        cin >> k;
        child[i].resize(k);
        for (int j = 0;j<k;j++){
            cin >> child[i][j];
            root[child[i][j]-1] = false;
        }
    }
    for (int i = 0;i<n;i++){
        if (root[i]){
            cout << (i+1) << "\n";
            dfs(i);
            cout << ans << "\n";
        }
    }
}


Python範例代碼:

2019年6月

互補CP

提示

題解
用位元來記錄狀態
用unordered_multiset存每一個CP
再用XOR判斷互補


C++範例代碼:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
int main(){
    ios::sync_with_stdio(0);cin.tie(0);
    ll m,n;
    cin >> m >> n;
    unordered_multiset<ll> dat;
    while (n--){
        string s;
        cin >> s;
        ll cur = 0;
        for (char c : s){
            ll i = c>='a'?((c-'a')+26):(c-'A');
            cur |= 1LL<<i;
        }
        dat.insert(cur);
    }
    ll ans = 0;
    ll full = (1LL<<m)-1LL;
    for (ll i : dat) ans += dat.count(full^i);
    cout << (ans/2) << "\n";
}


Python範例代碼:

2020年1月

砍樹

提示

題解
用linked list處理
由左往右掃描


C++範例代碼:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
int main(){
    ios::sync_with_stdio(0);cin.tie(0);
    ll n,l;
    cin >> n >> l;
    vector<ll> p(n);
    vector<ll> h(n);
    for(ll i = 0;i<n;i++)cin >> p[i];
    for(ll i = 0;i<n;i++)cin >> h[i];
    list<pair<ll,ll>> line;
    ll big = 1e18;
    line.push_back({0LL,big});
    for (ll i = 0;i<n;i++) line.push_back({p[i],h[i]});
    line.push_back({l,big});
    ll c=0, ans=0;
    auto it = line.begin();
    it++;
    auto ed = line.end();
    ed--;
    while (it!=ed){
        auto left = it;
        left--;
        auto right = it;
        right++;
        ll need = (*it).second;
        if ((*it).first-(*left).first >= need || (*right).first - (*it).first >= need){
            c++;
            ans = max(ans,need);
            line.erase(it);
            if (left!=line.begin()) it = left;
            else it = right;
        }else{
            it++;
        }
    }
    cout << c << "\n" << ans << "\n";
}


Python範例代碼:

2020年7月

圓環出口

提示
二分搜

題解
維護前綴和
對前綴和二分搜來計算目的地


C++範例代碼:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
int main(){
    ios::sync_with_stdio(0);cin.tie(0);
    ll n,m;
    cin >> n >> m;
    vector<ll> p(n);
    vector<ll> s(n+1,0);
    ll all = 0;
    for (int i = 0;i<n;i++){
        cin >> p[i];
        all+=p[i];
        s[i+1] = all;
    }
    ll cur = 0;
    while (m--){
        ll q;
        cin >> q;
        ll to = (s[cur+1]-p[cur]+q)%all;
        ll en = (lower_bound(s.begin(),s.end(),to)-s.begin())%n;
        cur = en;
    }
    cout << cur << "\n";
}


Python範例代碼:

2020年10月

勇者修煉

提示
dp

題解
逐行做dp,要由右往左也要由左往右


C++範例代碼:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
int main(){
    ios::sync_with_stdio(0);cin.tie(0);
    ll m,n;
    cin >> m >> n;
    vector<vector<ll>> a(m);
    vector<vector<ll>> l(m);
    vector<vector<ll>> r(m);
    for (ll i = 0;i<m;i++){
        a[i].resize(n);
        l[i].resize(n);
        r[i].resize(n);
        for (ll j = 0;j<n;j++){
            cin >> a[i][j];
            l[i][j] = r[i][j] = a[i][j];
        }
    }
    for (ll i = 0;i<m;i++){
        if (i>0){
            for (ll j = 0;j<n;j++){
                l[i][j] = r[i][j] = a[i][j] + max(0LL,max(l[i-1][j], r[i-1][j]));
            }
        }
        for (ll j = 1;j<n;j++){
            r[i][j] = max(r[i][j],a[i][j]+r[i][j-1]);
        }
        for (ll j=n-2;j>=0;j--){
            l[i][j] = max(l[i][j],a[i][j]+l[i][j+1]);
        }
    }
    ll ans = -1e18;
    for (ll j = 0;j<n;j++){
        ans = max(ans,max(l[m-1][j],r[m-1][j]));
    }
    cout << ans << "\n";
}


Python範例代碼:

2021年1月

切割費用

提示
set、二分搜

題解
用set維護切割點
並用二分搜取得所切割的棍子的長度


C++範例代碼:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
int main(){
    ios::sync_with_stdio(0);cin.tie(0);
    ll n,l;
    cin >> n >> l;
    vector<ll> cuts(n);
    for (ll z = 0;z<n;z++){
        ll x,i;
        cin >> x >> i;
        cuts[i-1]=x;
    }
    ll ans = 0;
    set<ll> s;
    s.insert(0);
    s.insert(l);
    for (ll c : cuts){
        set<ll>::iterator L = s.upper_bound(c);
        set<ll>::iterator R = s.lower_bound(c);
        L--;
        ans += ((*R)-(*L));
        s.insert(c);
    }
    cout << ans << "\n";
}


Python範例代碼:

2021年9月

幸運數字

提示

題解
用線段樹求最小值位置
再用前綴和處理總和


C++範例代碼:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
struct segtree{
    vector<pair<ll,ll>> dat;
    ll n;
    segtree(ll N){
        n = N;
        dat.resize(4*n);
    }
    void build(ll i, ll l, ll r, vector<ll>::iterator &it){
        if (l == r){
            dat[i] = {*it,l};
            it++;
        }else{
            ll mid = (l+r)>>1;
            build(i<<1,l,mid,it);
            build(i<<1|1,mid+1,r,it);
            dat[i] = min(dat[i<<1],dat[i<<1|1]);
        }
    }
    pair<ll,ll> query(ll i, ll l, ll r, ll ql, ll qr){
        if (ql<=l&&r<=qr){
            return dat[i];
        }else{
            ll mid = (l+r)>>1;
            if (qr<=mid) return query(i<<1,l,mid,ql,qr);
            if (ql>mid) return query(i<<1|1,mid+1,r,ql,qr);
            return min(query(i<<1,l,mid,ql,qr),query(i<<1|1,mid+1,r,ql,qr));
        }
    }
};
int main(){
    ios::sync_with_stdio(0);cin.tie(0);
    ll n;
    cin >> n;
    vector<ll> a(n);
    vector<ll> sum(n,0);
    for(ll i = 0;i<n;i++){
        cin >> a[i];
        sum[i+1] = sum[i]+a[i];
    }
    segtree s(n);
    auto it = a.begin();
    s.build(1,0,n-1,it);
    ll l = 0, r = n-1;
    while (l<r){
        ll m = s.query(1,0,n-1,l,r).second;
        if (m == l) l++;
        else if (m == r) r--;
        else{
            ll left = sum[m]-sum[l];
            ll right = sum[r+1]-sum[m+1];
            if (left>right) r=m-1;
            else l = m+1;
        }
    }
    cout << a[l] << "\n";
}


Python範例代碼:

2021年11月

生產線

提示
差值陣列、排序

題解
用差值陣列處理每一個位置需要的資料量
把資料量和機器速度分別排序後
的加在一起
透過排序不等式可以知道這樣是最好的


C++範例代碼:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
int main(){
    ios::sync_with_stdio(0);cin.tie(0);
    ll n, m;
    cin >> n >> m;
    vector<ll> d(n+1,0);
    while (m--){
        ll l,r,w;
        cin >> l >> r >> w;
        d[l-1]+=w;
        d[r]-=w;
    }
    vector<ll> a(n);
    ll cur = 0;
    for (ll i = 0;i<n;i++){
        cur+=d[i];
        a[i] = cur;
    }
    vector<ll> t(n);
    for (ll i = 0;i<n;i++) cin >> t[i];
    sort(a.begin(),a.end());
    sort(t.begin(),t.end());
    ll ans = 0;
    for (ll i = 0;i<n;i++){
        ans += a[n-1-i]*t[i];
    }
    cout<< ans << "\n";
}


Python範例代碼:

2022年1月

數位占卜

提示
set

題解
用hashset存籤
對於每一個籤,切出不同長度的前後綴,若前綴=後綴 且 剩下的部分能在set裡找到一樣的 則 答案+1

原理:
對於一組聖筊
不失一般性設比較長
所以a的前半a的後半加b一樣
a的前半=c、a的後半=d

可知的前後綴
所以對每一支籤,切出不同長度的前後綴,只要存在與剩下的部分相同的籤,就可以組成聖筊
因為總是用長的籤去找短的籤,所以不用擔心重複算到的問題


C++範例代碼:

#include<bits/stdc++.h>
using namespace std;
int main(){
    int n;
    cin >> n;
    unordered_set<string> dat;
    while (n--){
        string s;
        cin >> s;
        dat.insert(s);
    }
    long long ans = 0;
    for (string s: dat){
        int l = s.size();
        for (int i = 1;i<(l+1)/2;i++){
            bool sus = true;
            for (int j = 0;j<i;j++){
                if (s[j]!=s[j+l-i]) sus=false;
            }
            if (sus){
                ans += dat.count(s.substr(i,l-2*i))?1:0;
            }
        }
    }
    cout << ans << "\n";
}


Python範例代碼:

m = int(input())
dat = {input() for a in range(m)}
ans = 0
for s in dat:
    for i in range(1,(len(s)+1)//2):
        if s[:i]==s[-i:] and s[i:-i] in dat:
            ans+=1
print(ans)

2022年6月

雷射測試

提示
模擬、二分搜

題解
紀錄各行列上有哪些點有鏡子,並排序
再直接模擬,查詢的時候用二分搜


C++範例代碼:

#include<bits/stdc++.h>
using namespace std;
using ll = long long;
const ll sz = 30000;
int main(){
    ios::sync_with_stdio(0);cin.tie(0);
    ll n;
    cin >> n;
    vector<vector<pair<ll,ll>>> rows(sz*3),cols(sz*2);
    while (n--){
        ll x,y,c;
        cin >> x >> y >> c;
        y+=sz;
        rows[y].push_back({x,c});
        cols[x].push_back({y,c});
    }
    for (vector<pair<ll,ll>> &v : rows) if (v.size()>1) sort(v.begin(),v.end());
    for (vector<pair<ll,ll>> &v : cols) if (v.size()>1) sort(v.begin(),v.end());
    bool sus = true;
    ll facing = 1; // 1=positive 0=negative
    ll hv = 1; // 1=horizontal 0=vertical
    ll x = 0;
    ll y = sz;
    function<void(vector<pair<ll,ll>>&,ll&)> query = [&](vector<pair<ll,ll>>& line, ll& p){
        if (facing){
            vector<pair<ll,ll>>::iterator it = upper_bound(line.begin(),line.end(),make_pair(p,1LL));
            if (it!=line.end()){
                p = (*it).first;
                facing ^= (*it).second;
                sus = true;
            }
        }else{
            vector<pair<ll,ll>>::iterator it = lower_bound(line.begin(),line.end(),make_pair(p,0LL));
            if (it!=line.begin()){
                it--;
                p = (*it).first;
                facing ^= (*it).second;
                sus = true;
            }
        }
    };
    ll ans = 0;
    while (sus){
        sus = false;
        if (hv) query(rows[y],x);
        else query(cols[x],y);
        hv^=1;
        ans += sus;
    }
    cout << ans << "\n";
}


Python範例代碼: