• Educational Codeforces Round 65 (Rated for Div. 2)题解


    Educational Codeforces Round 65 (Rated for Div. 2)题解

    题目链接

    A. Telephone Number

    水题,代码如下:

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 2e5 + 5;
    int a[N] ;
    int n, T;
    char s[N] ;
    int main() {
        cin >> T;
        while(T--) {
            cin >> n;
            scanf("%s", s + 1) ;
            int fir = 1;
            for(;fir <= n; fir++) if(s[fir] == '8') break ;
            if(n - fir + 1 >= 11) cout << "YES" << '
    ' ;
            else cout << "NO" << '
    ' ;
        }
        return 0;
    }
    

    B. Lost Numbers

    现在cf B题都开始交互了= =。
    这个题直接询问((1,2),(2,3),(3,4),(4,5))即可解出前5个,剩下一个就确定了。
    判断是否成立的话我是直接随机的,毕竟长度比较小。
    代码如下:

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 10;
    // 0 3, 1 1  = 64
    int b[N][N] ;
    int c[N] ;
    int n, T;
    int main() {
        srand(time(NULL));
        for(int i = 1; i <= 4; i++) {
            printf("? %d %d
    ", i, i + 1);
            fflush(stdout);
            cin >> b[i - 1][i] ;
        }
        vector <int> a;
        a.push_back(4);
        a.push_back(8);
        a.push_back(15);
        a.push_back(16);
        a.push_back(23);
        a.push_back(42);
        while(1) {
            int f = 1;
            for(int i = 0; i < 4; i++) {
                if(a[i] * a[i + 1] != b[i][i + 1]) f = 0;
            }
            if(f) break ;
            random_shuffle(a.begin(), a.end());
        }
        printf("!");
        for(auto v : a) printf(" %d",v);
        printf("
    ") ;
        fflush(stdout) ;
        return 0;
    }
    

    C. News Distribution

    并查集水题,维护每个集合拥有元素的个数即可。
    代码如下:

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 5e5 + 5;
    int n, m;
    int f[N], sum[N];
    int find(int x) {
        return f[x] == x ? f[x] : f[x] = find(f[x]) ;
    }
    void Union(int x, int y) {
        int fx = find(x), fy = find(y) ;
        if(fx != fy) {
            f[fx] = fy;
            sum[fy] += sum[fx] ;
        }
    }
    int main() {
        ios::sync_with_stdio(false); cin.tie(0) ;
        cin >> n >> m;
        for(int i = 1; i <= n; i++) f[i] = i, sum[i] = 1;
        for(int i = 1; i <= m; i++) {
            int k, last = -1;
            cin >> k;
            int x;
            for(int i = 1; i <= k; i++) {
                cin >> x;
                if(last == -1) last = x;
                else Union(last, x) ;
            }
        }
        for(int i = 1; i <= n; i++) {
            int fa = find(i);
            cout << sum[fa] << ' ';
        }
        return 0;
    }
    

    D. Bicolored RBS

    我这个一开始也写的并查集来找,最后还是一直没有A。。。后面发现其实简单的,我原来的写法则要讨论很多的情况。
    因为给定的括号串一定是合法的,所以配对的两个括号它们所在位置的奇偶性一定是相同的。
    因为我们要求深度最大最小,那么我们对于左括号,一个给1,一个给0就行了,这样可以使得尽量两边都最小。如果对于一个')',给它的颜色为0,那么后面也接着从0开始染色,因为如果把中间已经匹配了的消去,那么就可以保证染色是1,0,1,0...这样。
    代码如下:

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 2e5 + 5;
    int n;
    char s[N] ;
    int main() {
        int c = 0;
        cin >> n;
        scanf("%s", s + 1) ;
        for(int i = 1; i <= n; i++) {
            if(s[i] == '(') {
                ++c;
                cout << (c & 1);
            } else {
                cout << (c & 1);
                --c;
            }
        }
        cout << '
    ' ;
        return 0;
    }
    

    E. Range Deleting

    删去值域为([l,r])的数后,如果剩下的数满足条件,那么我们就可以得到(down[l-1]<up[r+1]),这里的(down[i])表示值为i并且满足前面的值满足单调分布时的最大位置,(up[i])则表示最小位置。
    然后我们枚举枚举下界,来确定上界就行了,下界增大时,上界不会减小,所以复杂度是(O(n))的。
    这个题的关键就是能够想到维护值域的前缀、后缀信息。

    代码如下:

    Code
    #include <bits/stdc++.h>
    #define INF 0x3f3f3f3f;
    using namespace std;
    typedef long long ll;
    const int N = 1e6 + 5;
    int n, m;
    int a[N], up[N], dw[N], mn[N], mx[N];
    int main() {
        ios::sync_with_stdio(false); cin.tie(0);
        cin >> n >> m;
        for(int i = 1; i <= m; i++) mn[i] = n + 1;
        for(int i = 1; i <= n; i++) {
            cin >> a[i] ;
            mn[a[i]] = min(mn[a[i]], i) ;
            mx[a[i]] = max(mx[a[i]], i) ;
        }
        dw[0] = 0;
        for(int i = 1; i <= m; i++) {
            if(dw[i - 1] < mn[i]) {
                dw[i] = max(dw[i - 1], mx[i]) ;
            } else dw[i] = n + 2;
        }
        up[m + 1] = n + 1;
        for(int i = m; i >= 1; i--) {
            if(up[i + 1] > mx[i]) {
                up[i] = min(mn[i], up[i + 1]);
            } else up[i] = -1;
        }
        int j = 2;
        ll ans = 0;
        for(int i = 0; i < m; i++) {
            while(j <= m && (i + 1 >= j || dw[i] > up[j])) ++j;
            if(dw[i] < up[j]) ans += m - j + 2;
        }
        cout << ans;
        return 0;
    }
    

    F. Scalar Queries

    填坑来了...
    这是一个十分巧妙的题。反正我没想出来...
    题目中定义了(f(l,r)=sum_{i=1}^{r-l+1}b_i*i),这里的(b_i)就是原数组(a_1,a_2,cdots,a_n)中的(a_l,a_{l+1},cdots,a_{r})排序过后的值。
    然后题目要求计算(sum_{1leq lleq rleq n}f(l,r))

    直接考虑有点麻烦,我们就可以考虑每一个数的贡献。
    假设现在考虑的为(a_i),那么包含它的区间就有(i*(n-i+1))个,对于每一个区间,假设比(a_i)小的数的个数为(x_i),那么此时(a_i)的贡献就为((x_i+1)*a_i),我们对于每个区间的1提出来,那么总共就是(i*(n-i+1)*a_i)。易知最终的答案就为((sum_{j=1}^{i*(n-i+1)}x_j)*a_i)
    现在我们的任务就是统计包含(a_i)的所有区间中,比(a_i)小的数的个数
    此时我们也转换一下,考虑每一个数的贡献,对于在(a_i)左边的数,其贡献就为(j*(n-i+1));在右边的贡献就为(k*i),这里(j,k)分别指从左边开始第几个,从右边开始第几个。因为左右两边等价,我们分析左边:((n-i+1)*sum_{j=1}^{i}j*[a_j<a_i])
    右边求和的式子,我们用树状数组就可以解决了。

    关键在于这两次转化。cf题解里面全是用的数学公式来推导,其实也没有那么麻烦,但感觉这进一步加深了我对数学思维在竞赛中应用的理解吧...
    代码如下:

    Code
    #include <bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    const int N = 5e5 + 5, MOD = 1e9 + 7;
    int n;
    ll a[N], b[N], c[N];
    ll s[2][N] ;
    int lowbit(int x) {
        return x & (-x) ;
    }
    ll query(int x) {
        ll ans = 0;
        for(ll i = x; i > 0; i -= lowbit(i)) ans += c[i] ;
        return ans ;
    }
    void update(int x, int val) {
        for(int p = x; p < N; p += lowbit(p)) c[p] += val ;
    }
    void add(ll &x, ll y) {
        x += y;
        if(x >= MOD) x %= MOD ;
    }
    ll mul(ll x, ll y) {
        x *= y;
        if(x >= MOD) x %= MOD ;
        return x;
    }
    int main() {
        ios::sync_with_stdio(false);cin.tie(0);
        cin >> n;
        for(int i = 1; i <= n; i++) cin >> a[i], b[i] = a[i];
        sort(b + 1, b + n + 1);
        int D = unique(b + 1, b + n + 1) - b - 1;
        ll ans = 0;
        for(int k = 0; k < 2; k++) {
            memset(c, 0, sizeof(c)) ;
            for(int i = 1; i <= n; i++) {
                int p = lower_bound(b + 1, b + D + 1, a[i]) - b;
                s[k][i] = query(p) ;
                update(p, i) ;
            }
            reverse(a + 1, a + n + 1) ;
        }
        reverse(s[1] + 1, s[1] + n + 1) ;
        for(int i = 1; i <= n; i++) {
            add(ans, mul(a[i], mul(i, n - i + 1))) ;
            add(ans, mul(a[i], mul(s[0][i], n - i + 1))) ;
            add(ans, mul(a[i], mul(s[1][i], i))) ;
        }
        cout << ans;
        return 0;
    }
    
  • 相关阅读:
    无向图的双连通分量
    word发布博客
    构造泛型类型变量数组
    利用函数式接口和反射实例化泛型类型变量
    java8 lambda方法引用
    java8 常用函数式接口
    第一章 java程序设计概述
    算法基础~链表~从位置m到n逆序
    算法基础~链表【将链表逆序题(不可申请额外的空间)~头插法】
    Css定位的bug(margin导致的bug)
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/10878286.html
Copyright © 2020-2023  润新知