Atcoder ABC369

A - 369

给定两个数 a , b a,b a,b,求有几个数 c c c满足
a , b , c a,b,c a,b,c三个数组成的数列(顺序任意)为等差数列


签到题。只有三种顺序 a b c , c a b , a c b abc,cab,acb abc,cab,acb,求集合个数

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <map>
#include <set>
#include <vector>
#include <queue>
#include <stack>
#include <unordered_map>
#include <algorithm>

using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;



int main(){
    //freopen("in.txt", "r", stdin);
    int a, b;
    cin >> a >> b;
    set<int> s;
    s.insert(a * 2 - b);
    s.insert(b * 2 - a);
    if ((a + b) % 2 == 0) s.insert((a + b) / 2);
    cout << s.size() << endl;
    return 0;
}

B - Delimiter

给定 N N N长度的序列 A i , S i A_i,S_i Ai,Si
A i A_i Ai代表键的位置 S i S_i Si L L L时代表左手弹奏序列, R R R时代表右手序列
求最小化的左手移动距离+右手移动距离


签到题2。模拟,手的初始位置在第一个点上即可。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <map>
#include <set>
#include <vector>
#include <queue>
#include <stack>
#include <unordered_map>
#include <algorithm>

using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;

int n;
int l = -1, r = -1;


int main(){
    //freopen("in.txt", "r", stdin);
    cin >> n;
    int ans = 0;
    for (int i = 0; i < n; ++i) {
        int a;
        char c;
        cin >> a >> c;
        if (c == 'L') {
            if (l != -1) ans += abs(a - l);
            l = a;
        }
        else {
            if (r != -1) ans += abs(a - r);
            r = a;
        }
        
    }
    cout << ans << endl;
    return 0;
}

C - Count Arithmetic Subarrays

N N N长度序列 A i A_i Ai
求连续子串为等差数列的个数


签到题3 .对于每个元素记录和前一个值的差 d i d_i di,同时记录下当前等差数列的长度 c c c。如果之前的 d i − 1 d_{i-1} di1 d i d_i di相同那么 c + = 1 c+=1 c+=1否则 c = 1 c=1 c=1

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <map>
#include <set>
#include <vector>
#include <queue>
#include <stack>
#include <unordered_map>
#include <algorithm>

using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;

int n;
ll a[200020];
ll d;


int main(){
    //freopen("in.txt", "r", stdin);
    cin >> n;
    ll ans = n;
    for (int i = 1; i <= n; ++i) cin >> a[i];
    d = 1ll << 60;
    ll c = 0;
    for (int i = 2; i <= n; ++i) {
        if (a[i] - a[i - 1] == d) {
            c++;
        }
        else {
            d = a[i] - a[i - 1];
            c = 1;
        }
        ans += c;
    }
    cout << ans << endl;
    return 0;
}

D - Bonus EXP

N N N长度序列 A i A_i Ai中 任取一子序列记为 B B B
b 1 , b 2 , b 3 . . . b n b_1,b_2,b_3...b_n b1,b2,b3...bn B B B
s = b 1 + b 2 ∗ 2 + b 3 + b 4 ∗ 2 + . . . s=b_1+b_2*2+b_3+b_4*2+... s=b1+b22+b3+b42+...
最大化 s s s


奇偶递推
设当前取完位置 i i i时取奇数个元素得到的最大值为 f ( i , 1 ) f(i,1) f(i,1),偶数个元素得到最大值为 f ( i , 0 ) f(i,0) f(i,0)
f ( i , 1 ) = m a x ( f ( i − 1 , 0 ) + a i , f ( i − 1 , 1 ) ) f(i,1)=max(f(i-1,0)+a_i,f(i-1,1)) f(i,1)=max(f(i1,0)+ai,f(i1,1))
f ( i , 0 ) = m a x ( f ( i − 1 , 1 ) + a i ∗ 2 , f ( i − 1 , 0 ) ) f(i,0)=max(f(i-1,1)+a_i*2,f(i-1,0)) f(i,0)=max(f(i1,1)+ai2,f(i1,0))

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <map>
#include <set>
#include <vector>
#include <queue>
#include <stack>
#include <unordered_map>
#include <algorithm>

using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;


ll a[200020];
ll f[200020][2];
int n;

int main(){
    //freopen("in.txt", "r", stdin);
    cin >> n;
    for (int i = 1; i <= n; ++i) cin >> a[i];
    memset(f, 0xf7, sizeof(f));
    f[0][0] = 0;
    for (int i = 1; i <= n; ++i) {
        f[i][0] = max(f[i - 1][0], f[i - 1][1] + a[i] * 2);
        f[i][1] = max(f[i - 1][1], f[i - 1][0] + a[i]);
    }
    printf("%lld\n", max(f[n][0], f[n][1]));
    return 0;
}

E - Sightseeing Tour

无向图中给定 K K K条边,要求从点1经过这些边(顺序不定),最后到达点N的最短旅行距离


注意到 K ≤ 5 K \leq5 K5,那么枚举这些边的访问顺序和边内点的访问顺序,然后在不同的边之间用最短距离连接。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <map>
#include <set>
#include <vector>
#include <queue>
#include <stack>
#include <unordered_map>
#include <algorithm>

using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;

int n, m;
ll d[404][404];
struct Edge {
    int u, v;
    ll w;
};
Edge e[200020];
int q;


void solve() {
    int K;
    cin >> K;
    vi seq;
    while (K--) {
        int t;
        cin >> t;
        seq.push_back(t);
    }
    sort(seq.begin(), seq.end());
    int l = seq.size();
    ll ans = 1ll << 60;
    for (int i = 0; i < (1 << l); ++i) {
        do {
            int cur = 1;
            ll cost = 0;
            for (int j = 0; j < l; ++j) {
                int idx = seq[j];
                auto [u, v, w] = e[idx];
                if ((i >> j) & 1) swap(u, v);
                cost += d[cur][u] + w;
                cur = v;
            }
            cost += d[cur][n];
            ans = min(ans, cost);
            //printf("%lld\n", ans);
        } while (next_permutation(seq.begin(), seq.end()));
    }
    
    printf("%lld\n", ans);
}


int main(){
    //freopen("in.txt", "r", stdin);
    cin >> n >> m;
    memset(d, 0x3f, sizeof(d));
    for (int i = 1; i <= n; ++i) d[i][i] = 0;

    for (int i = 1; i <= m; ++i) {
        int u, v;
        ll w;
        cin >> u >> v >> w;
        d[u][v] = min(d[u][v], w);
        d[v][u] = min(d[v][u], w);
        e[i] = Edge({ u, v, w });
    }
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= n; ++j) {
            for (int k = 1; k <= n; ++k) {
                if (d[j][i] + d[i][k] < d[j][k]) d[j][k] = d[j][i] + d[i][k];
            }
        }
    }
    cin >> q;
    while (q--) {
        solve();
    }
    return 0;
}

F - Gather Coins

非常大的图中只能往右或者往下走,给定 N N N个点,求最多能走到几个点,并求路线


不错的500分题。
首先对点按照rc排序,然后dp。每个点坐标 R i , C i R_i,C_i Ri,Ci,要求一条经过当前点的路径能覆盖几个点,就是求之前从第1列到第c列的点中最多能覆盖几个点然后+1。即为一个区间查询最值问题。可以用线段树解。第二问求路径,可在线段树中记录下每一个最值代表的点索引,然后从后往前迭代求解。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <map>
#include <set>
#include <vector>
#include <queue>
#include <stack>
#include <unordered_map>
#include <algorithm>

using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;


int h, w, n;
const int N = 200002;
vector<pii> seq;

struct Node{
    int l, r;
    int mx;
    int idx;
};
Node tr[N << 2];
#define lu (u * 2)
#define ru (u * 2 + 1)


void build(int l, int r, int u) {
    tr[u].l = l, tr[u].r = r;
    if (l == r) return;
    int mi = (l + r) / 2;
    build(l, mi, lu);
    build(mi + 1, r, ru);
}


void push_up(int u) {
    if (tr[lu].mx > tr[ru].mx) {
        tr[u].mx = tr[lu].mx;
        tr[u].idx = tr[lu].idx;
    }
    else {
        tr[u].mx = tr[ru].mx;
        tr[u].idx = tr[ru].idx;
    }
}

void update(int idx, int y, int u, int val) {
    if (tr[u].l == tr[u].r) {
        tr[u].mx = val; tr[u].idx = idx;
        return;
    }
    int mi = (tr[u].l + tr[u].r) / 2;
    if (y <= mi) update(idx, y, lu, val);
    else update(idx, y, ru, val);
    push_up(u);
}

Node query(int l, int r, int u) {
    if (l <= tr[u].l && tr[u].r <= r) return tr[u];
    Node ret({ 0, 0, 0, 0 });
    int mi = (tr[u].l + tr[u].r) / 2;
    if (l <= mi) {
        Node ln = query(l, r, lu);
        if (ret.mx < ln.mx)
            ret = ln;
    }
    if (r > mi) {
        Node rn = query(l, r, ru);
        if (ret.mx < rn.mx)
            ret = rn;
    }
    return ret;
}


int main(){
    //freopen("in.txt", "r", stdin);
    cin >> h >> w >> n;
    for (int i = 1; i <= n; ++i) {
        int r, c;
        cin >> r >> c;
        seq.push_back({ r, c });
    }
    sort(seq.begin(), seq.end());
    build(1, w, 1);
    unordered_map<int, int> path;
    for (int i = 0; i < n; ++i) {
        auto [x, y] = seq[i];
        Node ret = query(1, y, 1);
        update(i + 1, y, 1, ret.mx + 1);
        path[i + 1] = ret.idx;
        //printf("%d %d\n", i + 1, ret.idx);
    }
    Node rt = query(1, w, 1);
    int ans = rt.mx;
    printf("%d\n", ans);
    int lst = rt.idx;
    string s = "";
    int cx = h, cy = w;
    while (lst != 0) {
        auto [x, y] = seq[lst - 1];
        for (int i = 0; i < cx - x; ++i) s += "D";
        for (int i = 0; i < cy - y; ++i) s += "R";
        lst = path[lst];
        cx = x, cy = y;
    }
    for (int i = 0; i < cx - 1; ++i) s += "D";
    for (int i = 0; i < cy - 1; ++i) s += "R";
    reverse(s.begin(), s.end());
    cout << s << endl;
    return 0;
}

但是线段树很费时间,有更简单的做法吗?
答案是有。
观察rc排序后的坐标点对,很容易发现这就是一个求最大不下降子序列问题。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <map>
#include <set>
#include <vector>
#include <queue>
#include <stack>
#include <unordered_map>
#include <algorithm>

using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;

int h, w, n;

pii seq[200002];
int pre[200002];


int main(){
    //freopen("in.txt", "r", stdin);
    cin >> h >> w >> n;
    for (int i = 1; i <= n; ++i) {
        int x, y;
        cin >> x >> y;
        seq[i] = { x, y };
    }
    sort(seq + 1, seq + n + 1);
    vi b, p;

    for (int i = 1; i <= n; ++i) {
        auto [x, y] = seq[i];
        int pos = upper_bound(b.begin(), b.end(), y) - b.begin();
        if (pos == b.size()) {
            b.push_back(y);
            p.push_back(i);
        }
        else {
            b[pos] = y;
            p[pos] = i;
        }
        if(pos - 1 >= 0)
            pre[i] = p[pos - 1];
    }
    printf("%d\n", b.size());
    string s;
    int cx = h, cy = w;
    int pos = p.back();
    while (pos != 0) {
        auto [x, y] = seq[pos];
        for (int i = 0; i < cx - x; ++i) s += "D";
        for (int i = 0; i < cy - y; ++i) s += "R";
        pos = pre[pos];
        cx = x, cy = y;
    }
    for (int i = 0; i < cx - 1; ++i) s += "D";
    for (int i = 0; i < cy - 1; ++i) s += "R";
    reverse(s.begin(), s.end());
    cout << s << endl;
    return 0;
}

G - As far as possible

给出一颗边上有权重 L i Li Li的树 T T T
对于每个 1... N 1...N 1...N的值 K K K
在树上选出 K K K个点,最大化从点1访问这些点并且最后需要回到点1的最短路径


K = 1 K=1 K=1时,明显是选从1作为根节点开始访问,最长路径的叶子结点
之后显然需要选取叶子节点。当然不能算出所有叶子节点的路径长度然后排序。
设dfs(u)计算u节点到叶子节点的最长路径, d f s ( u ) = max ⁡ ( d f s ( v ) + w ( u , v ) ) dfs(u)=\max (dfs(v)+w(u,v)) dfs(u)=max(dfs(v)+w(u,v))
那么那些不在最长路径上的v就是后面可以插入的路径。
最后把这些路径值排序,依次加入答案中。
那些在最长路径上的v对于答案共享为0.

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <map>
#include <set>
#include <vector>
#include <queue>
#include <stack>
#include <unordered_map>
#include <algorithm>

using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;

int n;
vector<pair<int, ll>> g[200002];
vector<ll> ans;


ll dfs(int u, int f) {
    ll mx = 0;
    for (auto [v, w] : g[u]) {
        if (v == f)
            continue;
        ll t = dfs(v, u) + w;
        if (t > mx) {
            ans.push_back(mx);
            mx = t;
        }
        else {
            ans.push_back(t);
        }
    }
    return mx;
}

int main(){
    freopen("in.txt", "r", stdin);
    cin >> n;
    for (int i = 0; i < n - 1; ++i) {
        int u, v;
        ll w;
        cin >> u >> v >> w;
        g[u].push_back({ v, w });
        g[v].push_back({ u, w });
    }
    ans.push_back(dfs(1, 0));
    sort(ans.begin(), ans.end(), greater<ll> ());
    ll t = 0;
    for (ll x : ans) {
        t += x;
        printf("%lld\n", t * 2);
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值