zoukankan      html  css  js  c++  java
  • AtCoder Regular Contest 103

    传送门

    C - ////

    题意:
    给出一个序列({a_i}),先要求其满足以下条件:

    • (a_i=a_{i+2})
    • 共有两个不同的数

    你现在可以修改任意个数,现问最少修改个数为多少。

    思路:

    • 很明显奇偶分类。
    • 记录奇数位置、偶数位置的最大值和最大出现次数的情况;
    • 因为要求两个数不相同,所以还要维护次大。

    注意以下细节就是了。

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1e5 + 5;
    int n;
    int a[N], c[N];
    int mx[4], mxv[4];
    void add(int x, int p) {
        ++c[x];
        if(c[x] > mx[p]) {
            mx[p] = c[x];
            mxv[p] = x;
        } else if(c[x] > mx[p + 1]) {
            mx[p + 1] = c[x];
            mxv[p + 1] = x;
        }
    }
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> n;
        for(int i = 1; i <= n; i++) cin >> a[i];
        for(int i = 1; i <= n; i += 2) add(a[i], 0);
        for(int i = 1; i < N; i++) c[i] = 0;
        for(int i = 2; i <= n; i += 2) add(a[i], 2);
        int ans = n;
        if(mxv[0] == mxv[2]) {
            if(mx[1] > mx[3]) {
                ans -= mx[1] + mx[2];
            } else if(mx[1] < mx[3]){
                ans -= mx[0] + mx[3];
            } else if(mx[1] == mx[3] && !mx[1]) {
                ans -= n / 2;
            } else {
                ans -= mx[0] + mx[3];
            }
        } else {
            ans -= mx[0] + mx[2];
        }
        cout << ans;
        return 0;
    }
    
    

    D - Robot Arms

    题意:
    给出平面直角坐标系下的(n)个坐标点((x_i,y_i)),现要求你确定(m)个数((mleq 40)),并且(n)种方案。在第(i)种方案中,确定一个行走方向的序列(s),使得每一步走(a_i)的长度能够到达((x_i,y_i))

    思路:

    • 考虑二进制拆分。
    • 因为所有(a_i)和的奇偶性不变,即合法情况中,(x_i+y_i)的奇偶性也得相同。
    • 但是怎么确定方向?直接来拆显然不行,比如遇到(0)的情况。
    • 观察到对于(a=1)时,能走的范围是一个斜放的正方形;同理,当(a=1,2)时,形状没有变只是边长什么的增加了。
    • 所以对于(2^0,2^1,cdots,2^i),能走的区域有一个限制;如果达到(2^{i+1}),这个限制可以被突破,但依然有限制。
    • 但怎么才能有一种方案能走到目标点?
    • 考虑对称操作,即如果让目标点走到原点,从原点出发对称走,也能走到目标点。
    • 所以只要从目标点往回走,每步保证合法(在限制之内)即可。可以证明,最后一定能走回原点。

    上面分析的只是(x_i+y_i)为奇数的情况,为偶数的话我们先让其往右边走一格,以((1,0))为原点来搞即可。

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1005;
    
    int n, m;
    int X[N], Y[N];
    ll d[N];
    
    const int dx[5] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};
    const char dc[5] = {'R', 'L', 'U', 'D'};
    
    void getp(ll x, ll y) {
        for(int i = m; i; i--) {
            int now = d[i];
            for(int j = 0; j < 4; j++) {
                ll curx = x + d[i] * dx[j], cury = y + d[i] * dy[j];
                if(abs(curx) + abs(cury) < now) {
                    cout << dc[j];
                    x = curx, y = cury;
                    break;
                }
            }
        }
    }
    
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> n;
        int sign = 1, mx = 0;
        for(int i = 1; i <= n; i++) cin >> X[i] >> Y[i];
        for(int i = 2; i <= n; i++) {
            if((abs(X[i] + Y[i]) & 1) != (abs(X[i - 1] + Y[i - 1]) & 1))
                sign = 0;
            mx = max(mx, abs(X[i]) + abs(Y[i]));
        }
        if(!sign) {cout << -1; return 0;}
        for(d[m = 1] = 1; d[m] << 1 <= mx; ++m, d[m] = d[m - 1] << 1);
        if(abs(X[1] + Y[1]) & 1) sign = 0;
        cout << m + sign << '
    ';
        if(sign) cout << 1 << ' ';
        for(int i = m; i; i--) cout << d[i] << " 
    "[i == 1];
        for(int i = 1; i <= n; i++) {
            if(sign) cout << 'R';
            getp(X[i] - sign, Y[i]);
            cout << '
    ';
        }
        return 0;
    }
    
    

    E - Tr/ee

    题意:
    给出一个(01)序列(s),现要你构造一棵树满足:若第(i)个位置为(1),即表示这棵树能够通过去掉一条边得到size为(i)的子树;否则为(0)则表示不能。

    思路:
    不合法情况的判断:

    • (s_1)一定为(1)(s_n)一定为(0);
    • (s_i=s_{n-i})

    若不满足上面条件则为不合法。
    否则,注意到(s_1)一定为(1),并且(s_i=s_{n-i}),那么就可以构造出一颗类似"毛毛虫"的树,存在一条路径作为"body",路径上面的每个结点可能有若干"root"这样的树。
    这棵树满足,无论割哪条边,都可以满足条件,具体构造可以看代码。

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 1e5 + 5;
    
    int n;
    char s[N];
    
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> s + 1;
        n = strlen(s + 1);
        if(s[1] == '0' || s[n] == '1') {cout << -1; return 0;}
        for(int i = 1; i < n; i++) if(s[i] != s[n - i]) {
            cout << -1; return 0;
        }
        s[n] = '1';
        int rt = 1;
        for(int i = 2; i <= n; i++) if(s[i] == '1') {
            cout << i << ' ' << rt << '
    ';
            for(int j = rt + 1; j < i; j++) cout << i << ' ' << j << '
    ';
            rt = i;
        }
        return 0;
    }
    
    

    F - Distance Sums

    题意:
    构造一颗有(n)个结点的树,并且给出(D_i),这棵树要满足第(i)个结点与其它结点距离的累和为(D_i)
    如果可以构造,输出方案;否则输出(-1)

    思路:
    很显然且很重要的一个观察:

    • 叶子结点的(D)肯定最大,也就是说,如果从树的底部向上进行拓扑,肯定结点的(D)会不断减小。

    对于儿子和父亲来说,它们之间满足:(D_{fa}=D_{son}-sz_{fa}+sz_{son}=D_{son}+2sz_{son}-n)
    之后按(D)值从大到小搞即可,并且合并结点。
    那么我们就能判断对于(i=2,3,cdots,n)(D_i)是否合法,我们还不能判断(D_1)是否合法。
    所以最后再暴力(dfs)判以下即可。

    Code
    #include <bits/stdc++.h>
    #define MP make_pair
    #define fi first
    #define se second
    using namespace std;
    typedef long long ll;
    typedef pair<ll, int> pli;
    const int N = 1e5 + 5;
    
    int n;
    ll D[N];
    pli a[N];
    int sz[N];
    
    vector <int> g[N];
    
    void add(int x, int y) {
        sz[x] += sz[y];
        g[x].push_back(y);
    }
    
    ll res;
    
    void dfs(int u, int fa, ll d) {
        res += d;
        for(auto v : g[u]) {
            if(v != fa) dfs(v, u, d + 1);
        }
    }
    
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> n;
        for(int i = 1; i <= n; i++) cin >> D[i];
        for(int i = 1; i <= n; i++) {
            a[i] = MP(D[i], i); sz[i] = 1;
        }
        sort(a + 1, a + n + 1);
        int f = 1;
        for(int i = n; i > 1 && f; i--) {
            ll t = a[i].fi + 2ll * sz[a[i].se] - n;
            int x = lower_bound(a + 1, a + i, MP(t, 0)) - a;
            if(a[x].fi != t) f = 0;
            add(a[x].se, a[i].se);
        }
        if(f == 0) {cout << -1; return 0;}
        dfs(a[1].se, 0, 0);
        if(res != a[1].fi) {cout << -1; return 0;}
        for(int i = 1; i <= n; i++) {
            for(auto it : g[i]) cout << i << ' ' << it << '
    ';
        }
        return 0;
    }
    
    
  • 相关阅读:
    10/28总结
    10/27总结
    10/26总结
    10/25总结
    10/24总结
    毕业设计第二周整理规划
    毕业设计第一周第五天完成情况汇总
    毕业设计第一周第四天完成情况汇总
    毕业设计第一周第三天完成情况汇总
    毕业设计第一周第二天完成情况汇总
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/11461819.html
Copyright © 2011-2022 走看看