• Codeforces Edu Round 55 A-E


    A. Vasya and Book

    简单的取余运用。

    #include <iostream>
    #include <cstdio>
    #include <limits.h>
    #include <math.h>
    using namespace std;
    const int INF = INT_MAX;
    int main(){
        int T; scanf("%d", &T);
        while(T--){
            int n, x, y, d;
            scanf("%d%d%d%d", &n, &x, &y, &d);
            int res = INF;
        
            if(abs(x - y) % d == 0) res = min(res, abs(x - y) / d);
            if((y - 1) % d == 0) res = min(res, (int)ceil((x - 1.0) / d) + (y - 1) / d);
            if((n - y) % d == 0) res = min(res, (int)ceil((double)(n - x) / d) + (n - y) / d);
            printf("%d
    ", res == INF ? -1 : res);
        }
        return 0;
    }
    

    B. Vova and Trophies

    对于每一个(S)在的位置,二分出它的最大连续前缀(lenLeft)和最大连续后缀(lenRight)

    1. 如果除了这两块前缀以外没有别的(G)了,只需把右边的最后一个移到中间来,用(lenLeft + lenRight)更新
    2. 否则,可以把别的地方的(G)移动过来,用(lenLeft + lenRight + 1)更新

    对于全字符串都是(G)的情况特判即可。

    找最大前缀可以用前缀和维护(sum[i]) 代表([1, i])(G)的个数。

    总复杂度(O(nlogn)),后来发现题解全是(O(n))的,有点自闭了...

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    const int N = 100010;
    int n, sum[N], ans;
    char s[N];
    int main(){
        scanf("%d%s", &n, s + 1);
        for(int i = 1; i <= n; i++){
            sum[i] = sum[i - 1];
            if(s[i] == 'G') sum[i]++;
        }
        for(int i = 1; i <= n; i++){
            if(s[i] == 'S'){
                int ansLeft, ansRight;
                int lenLeft, lenRight;
                //获取左边最长
                int l = 0, r = (i - 1);
                while(l < r){
                    int mid = (l + r + 1) >> 1;
                    int left = (i - 1) - mid + 1;
                    if(sum[i - 1] - sum[left - 1] == mid) l = mid;
                    else r = mid - 1;
                }
                lenLeft = r; ansLeft = (i - 1) - r + 1;
    
                //右边最长
                l = 0, r = n - (i + 1) + 1;
                while(l < r){
                    int mid = (l + r + 1) >> 1;
                    int right = (i + 1) + mid - 1;
                    if(sum[right] - sum[i] == mid) l = mid;
                    else r = mid - 1;
                }
                lenRight = r; ansRight = (i + 1) + r - 1;
    
                if(sum[ansLeft - 1] || sum[n] - sum[ansRight]) 
                    ans = max(ans, lenRight + lenLeft + 1);
                else ans = max(ans, lenLeft + lenRight);
            }
            
        }
        if(sum[n] == n) ans = n;
        printf("%d
    ", ans);
        return 0;
    }
    

    C. Multi-Subject Competition

    正面思考似乎复杂度过高,会达到(O(nm))级别。我们不妨逆向思维,想想每个组对选(x)个的贡献。

    如果小组的大小$ >= x(,则可以加入前)x$大的数(前提 $ > 0()到)ans[x]$中。

    可以从小到大考虑,在算前(x)大的数(sum)时先计算前(x - 1)大的数的和,这样就可以线性解决了。

    时间复杂度(O(max(m, n)))

    #include <iostream>
    #include <cstdio>
    #include <vector>
    #include <algorithm>
    #include <cmath>
    using namespace std;
    const int N = 100010;
    int n, m, s[N], r[N], ans[N], maxn = -1;
    vector<int> G[N], sum[N];
    int main(){
        scanf("%d%d", &n, &m);
        for(int i = 1; i <= n; i++) {
            scanf("%d%d", s + i, r + i);
            G[s[i]].push_back(r[i]);
        }
        for(int i = 1; i <= m; i++) 
            sort(G[i].begin(), G[i].end(), greater<int>() );
        
        for(int i = 1; i <= m; i++){
            int sum = 0;
            maxn = max(maxn, (int)G[i].size());
            for(int j = 0; j < G[i].size(); j++){
                sum += G[i][j];
                if(sum > 0)ans[j + 1] += sum;
            }
        }
        int res = 0;
        for(int i = 1; i <= maxn; i++) res = max(res, ans[i]);
        printf("%d", res);
        return 0;
    }
    

    D. Maximum Diameter Graph

    1. 将满足(a[i] >= 2)的点合成一条链。

    2. 如果 (a[i] = 1) 的集合中还有数,就分别连向链的头和尾

    3. 如果(a[i] = 1)的集合非空,那么将(i)点和链上一点满足(a[x] > 0)(x)连边,然后把(a[x]—),中途如果链的可加入的点不够立即输出(NO),然后结束程序。

    写程序中想错了一些东西,所以用了优先队列,其实完全不需要…

    整个时间复杂度可以达到线性的(O(n))...

    #include <iostream>
    #include <cstdio>
    #include <set>
    #include <vector>
    #include <queue>
    using namespace std;
    typedef pair<int, int> PII;
    const int N = 510;
    int n, a[N], cnt = 0, f[N], head[N], numE = 0;
    struct Edge{
        int next, from, to;
    }e[N * N];
    void addEdge(int from, int to){
        e[++numE].next = head[from];
        e[numE].to = to;
        e[numE].from = from;
        head[from] = numE;
    }
    priority_queue<PII> q;
    queue<int> list;
    int main(){
        scanf("%d", &n);
        int last = -1, first = 0, res = 0;
        for(int i = 1; i <= n; i++){
            scanf("%d", a + i);
            if(a[i] >= 2) {
                if(last != -1) addEdge(i, last), addEdge(last, i), res++;
                last = i;
                if(!first) first = i;
                if(a[i] - 2 > 0) q.push(make_pair(a[i] - 2, i));
            }else{
                list.push(i);
            }
        }
        if(list.size()){
            addEdge(list.front(), first);
            addEdge(first, list.front());
            list.pop(); res++;
        }
            
        if(list.size()){
            addEdge(list.front(), last);
            addEdge(last, list.front());
            list.pop(); res++;
        }
            
        while(!list.empty()){
            int u = list.front(); list.pop();
            if(q.empty()) { puts("NO"); return 0; } 
            else{
                PII t = q.top(); q.pop();
                addEdge(u, t.second);
                addEdge(t.second, u);
                if(t.first > 1) q.push(make_pair(t.first - 1, t.second));
            }
        }
        printf("YES %d
    %d
    ", res, numE >> 1);
        for(int i = 1; i <= numE; i += 2){
            printf("%d %d
    ", e[i].from, e[i].to);
        }
        return 0;
    }
    

    E. Increasing Frequency

    …太难啦…

    (sum[x][i])([1, i])区间内(x)数字出现次数。

    我可以在([l, r])区间内把数(x)都加上(c - x),但是在([l, r])中原本是(c)的就没有了

    修改后全序列(c)数次数的变化量,代表相对于之前(c)数存在的个数,到答案我增加了多少。

    (ans[x]) 表示把数字(x)变成(c)的最大的变化量(也就是选择不同([l, r])造成的最大的变化量)

    可以写成:

    (ans[x] = max{sum[x][r] - sum[x][l - 1] - (sum[c][r] - sum[c][l - 1])} (1 <= l <= r <= n))

    我们可以枚举(r)的位置,找到一个(l)使其最小。

    这个式子可以转变为:

    (ans[x] = max{sum[x][r] - sum[c][r] - (sum[x][l - 1] - sum[c][l - 1])} (1 <= l <= r <= n))

    由于(l <= r) 我们可以考虑在枚举(r)的位置的同时维护一个最小值。

    (min[x][i]) 表示([1, i]) 这段的(min{sum[x][j] - sum[c][j]} (1 <= j <= i))

    那么变化量就可以写成:

    (ans[x] = max{sum[x][r] - sum[c][r] - min[x][r]})(1 <= r <= n)

    我们发现可以滚动掉(sum)数组与(min)数组的第二维度…于是,空间和时间的问题都完美解决啦...

    #include <cstdio>
    #include <iostream>
    #include <cmath>
    using namespace std;
    const int N = 500010;
    int n, c, a[N], ans[N], sum[N], minn[N];
    int main(){
        scanf("%d%d", &n, &c);
        for(int i = 1; i <= n; i++) scanf("%d", a + i);
        for(int i = 1; i <= n; i++){
            minn[a[i]] = min(minn[a[i]], sum[a[i]] - sum[c]);
            sum[a[i]]++;
            ans[i] = max(ans[i], sum[a[i]] - sum[c] - minn[a[i]]);
        }
        int res = sum[c];
        for(int i = 1; i <= n; i++)
            res = max(res, sum[c] + ans[i]);
        printf("%d", res);
        return 0;
    }
    
  • 相关阅读:
    fastjson基本使用 (待继续完善)【原】
    webkit内核浏览器的CSS写法
    sencha touch pull-refresh-panel 面板下拉刷新
    一个非常棒的星星评分插件
    JavaScript字符转Unicode,顺便说句:GitHub的Oh no页面很亮
    Git版本控制软件结合GitHub从入门到精通常用命令学习手册
    手把手教你如何加入到github的开源世界!
    前端面试题:高效地随机选取数组中的元素
    CSS3 动画animation
    CSS3 Transform
  • 原文地址:https://www.cnblogs.com/dmoransky/p/11279957.html
Copyright © 2020-2023  润新知