zoukankan      html  css  js  c++  java
  • BUPT2017 springtraining(16) #2 ——基础数据结构

    题目在这里

    A.似乎是个并查集+???

    B.10W的范围,似乎可以暴力来一发二分+sort?

    但我猜正解可以O(nlogn)?

    C.单调队列入门题目

    #include <cstdio>
    
    int n, m, a[1111111], ans1[1111111], ans2[1111111];
    
    struct queue_1 {
        int q[1111111];
        int l, r;
        queue_1(): l(1), r(0) {}
        int front() {
            return q[l];
        }
        void push(int i) {
            while(l <= r && q[l] + m <= i)   l ++;
            while(r >= l && a[i] <= a[q[r]]) r --;
            q[++ r] = i;
        }
    }q1;
    
    struct queue_2 {
        int q[1111111];
        int l, r;
        queue_2(): l(1), r(0) {}
        int front() {
            return q[l];
        }
        void push(int i) {
            while(l <= r && q[l] + m <= i)   l ++;
            while(r >= l && a[i] >= a[q[r]]) r --;
            q[++ r] = i;
        }
    }q2;
    
    void getint(int &x) {
        x = 0;
        static int c, f;
        while(c = getchar(), (c < '0' || c > '9') && c != '-');
        if(c != '-') x = c - '0', f = 0;
        else f = 1, x = 0;
        while(c = getchar(), c >= '0'&&c <= '9') x = x * 10 + c - '0';
        if(f) x = -x;
    }
    
    int main() {
        getint(n), getint(m);
        for(int i = 1;i < m;i ++) getint(a[i]), q1.push(i), q2.push(i);
        for(int i = m;i <= n;i ++) 
            getint(a[i]), q1.push(i), q2.push(i),
            ans1[++ ans1[0]] = a[q1.front()], ans2[++ ans2[0]] = a[q2.front()];
        for(int i = 1;i <= ans1[0];i ++) printf("%d ", ans1[i]);puts("");
        for(int i = 1;i <= ans2[0];i ++) printf("%d ", ans2[i]);
        return 0;
    }
    View Code

    我怎么就脑子抽了写了个极丑的封装呢

    注意选择C++,G++会超时

    D.简单的单点修改+区间查询max

    线段树即可,据说这题还能有树状数组的骚操作?

    比较懒,写了两个好写一点的

    分块

    #include <cstdio>
    
    int n, m, siz;
    int a[200010], mmax[500];
    
    int max(int x, int y){
        return x > y ? x : y;
    }
    
    int main() {
        int x, y, z;
        char str[3];
        while(scanf("%d %d", &n, &m) != EOF) {
            for(siz = 1;siz * siz < n;siz ++);
            for(int i = 0;i <= n / siz;i ++) mmax[i] = 0;
            for(int i = 0;i < n;i ++)
                scanf("%d", &a[i]), mmax[i / siz] = max(mmax[i / siz], a[i]);
            while(m --) {
                scanf("%s %d %d", str, &x, &y);
                if(str[0] == 'Q') {
                    z = 0, x --, y --;
                    if(x / siz == y / siz) {
                        for(int i = x;i <= y;i ++)
                            z = max(z, a[i]);
                    }
                    else {
                        for(int i = x;i < x / siz * siz + siz;i ++)
                            z = max(z, a[i]);
                        for(int i = y / siz * siz;i <= y;i ++)
                            z = max(z, a[i]);
                        for(int i = x / siz + 1;i < y / siz;i ++)
                            z = max(z, mmax[i]);
                    }
                    printf("%d
    ", z);
                }
                else {
                    x --, a[x] = y, mmax[x / siz] = 0;
                    for(int i = x / siz * siz;i < x / siz * siz + siz;i ++)
                        mmax[i / siz] = max(mmax[i / siz], a[i]);
                }
            }
        }
    }
    View Code

    zkw线段树

    #include <cstdio>
    
    int n, m, M;
    int tr[600010];
    
    int max(int x, int y){
        return x > y ? x : y;
    }
    
    int main() {
        int x, y, z;
        char str[3];
        while(scanf("%d %d", &n, &m) != EOF) {
            for(M = 1;M < n + 2;M <<= 1);
            for(int i = M + 1;i <= M + n;i ++) scanf("%d", &tr[i]);
            for(int i = M + n + 1;i <= (M << 1);i ++) tr[i] = 0;
            for(int i = M;i >= 1;i --) tr[i] = max(tr[i << 1], tr[i << 1 | 1]);
            while(m --) {
                scanf("%s %d %d", str, &x, &y);
                if(str[0] == 'Q') {
                    z = 0;
                    for(int s = x + M - 1, t = y + M + 1;s ^ t ^ 1;s >>= 1, t >>= 1) {
                        if(~s & 1) z = max(z, tr[s ^ 1]);
                        if( t & 1) z = max(z, tr[t ^ 1]);
                    }
                    printf("%d
    ", z);
                }
                else {
                    for(tr[x += M] = y, x >>= 1;x;x >>= 1)
                        tr[x] = max(tr[x << 1], tr[x << 1 | 1]);
                }
            }
        }
    }
    View Code

    多组数据的话,基本注意每次都clear一下就好了

    另外分块更滋磁下标从0开始计算

    但是如果题目明确标号从1-n就要另外注意一下了

    E.一个简单的队列套队列,代码供参考

    #include <queue>
    #include <cstdio>
    #include <iostream>
    
    #define rep(i, j, k) for(int i = j;i <= k;i ++)
    
    using namespace std;
    
    queue <int> q, a[1111];
    
    int n, bel[1111111];
    
    int main() {
        int m, x, t = 0;
        char str[10];
        ios::sync_with_stdio(false);
        while(cin >> n, n != 0) {
            printf("Scenario #%d
    ", ++ t);
            rep(i, 1, n) {
                cin >> m;
                rep(j, 1, m) cin >> x, bel[x] = i;
            } 
            while(cin >> str, str[0] != 'S') {
                if(str[0] == 'E') {
                    cin >> x;
                    if(!a[bel[x]].size()) q.push(bel[x]); 
                    a[bel[x]].push(x);
                }
                else {
                    printf("%d
    ", a[q.front()].front());
                    a[q.front()].pop();
                    if(!a[q.front()].size()) q.pop();
                }
            }
            while(!q.empty()) q.pop();
            rep(i, 1, n) while(!a[i].empty()) a[i].pop();
            puts("");
        } 
    }
    View Code

    F.无修改区间最值问题,直接上线段树(我写的zkw

    #include <cstdio>
    
    int n, m, M, tr[2][666666];
    
    int min(int x, int y) {
        return x < y ? x : y;
    }
    
    int max(int x, int y) {
        return x > y ? x : y;
    }
    
    int main() {
        int s, t, r, l;
        scanf("%d %d", &n, &m);
        for(M = 1;M < n + 2; M <<= 1);
        for(int i = M + 1;i <= M + n;i ++) scanf("%d", &tr[0][i]), tr[1][i] = tr[0][i];
        for(int i = M + n + 1;i <= M << 1;i ++) tr[1][i] = 6666666;
        for(int i = M;i;i --) 
            tr[0][i] = max(tr[0][i << 1], tr[0][i << 1 | 1]),
            tr[1][i] = min(tr[1][i << 1], tr[1][i << 1 | 1]);
        while(m --) {
            scanf("%d %d", &s, &t), r = 0, l = 6666666;
            for(s += M - 1, t += M + 1;s ^ t ^ 1;s >>= 1, t >>= 1) {
                if(~ s & 1) r = max(r, tr[0][s ^ 1]), l = min(l, tr[1][s ^ 1]);
                if(  t & 1) r = max(r, tr[0][t ^ 1]), l = min(l, tr[1][t ^ 1]);
            }
            printf("%d
    ", r - l);
        }
        return 0;
    }
    View Code

    G.并查集+堆的启发式合并

    总之手写堆大概只是用来理解其原理的

    平时写题又不卡常数还是priority_queue大法好啊

    顶多撸个简单的 '<' 重载美滋滋

    #include <queue>
    #include <cstdio>
    
    using std::queue;
    using std::priority_queue;
    
    priority_queue <int> q[100010];
    
    int n, m, f[100010], a[100010];
    
    void swap(int &x, int &y) {
        x ^= y,  y ^= x, x ^= y;
    }
    
    int find_(int x) {
        if(f[x] != x) return f[x] = find_(f[x]);
        return x;
    }
    
    int main() {
        int x, y, z;
        while(scanf("%d", &n) != EOF) {
            for(int i = 1;i <= n;i ++)
                f[i] = i, scanf("%d", &a[i]), q[i].push(a[i]);
            scanf("%d", &m);
            while(m -- ){
                scanf("%d %d", &x, &y);
                x = find_(x), y = find_(y);
                if(x == y) puts("-1");
                else {
                    if(q[x].size() < q[y].size()) swap(x, y);
                    z = q[x].top(), q[x].pop(), q[x].push(z >> 1);
                    z = q[y].top(), q[y].pop(), q[y].push(z >> 1);
                    f[y] = x;
                    while(!q[y].empty()) q[x].push(q[y].top()), q[y].pop();
                    printf("%d
    ", q[x].top());
                 }
            }
            for(int i = 1;i <= n;i ++)
                while(!q[i].empty()) q[i].pop();
        }
    }
    View Code

    H.把如何锯木头看成如何拼木头

    就是个石子合并问题,或者说哈夫曼树构造问题

    #include <iostream>
    #include <queue>
    
    #define rep(i, j, k) for(int i = j;i <= k;i ++)
    
    #define rev(i, j, k) for(int i = j;i >= k;i --)
    
    using namespace std;
    
    typedef long long ll;
    
    priority_queue <ll, vector<ll>, greater<ll> > q;
    
    ll ans, a, w;
    
    int n;
    
    int main() {
        ios::sync_with_stdio(false);
        cin >> n;
        rep(i, 1, n) cin >> a, q.push(a);
        rep(i, 2, n) w = q.top(), q.pop(), w += q.top(), q.pop(), ans += w, q.push(w); 
        cout << ans;
        return 0;
    }
    View Code

    I.很裸的并查集

    我们用 i 和 i + n 来代表虫 i 的两个相反性别

    而如果有 x 和 y 交配,那么显然有

    x 和 y + n 为同一性别

    y 和 x + n 为同一性别

    即 union(x, y + n), union(y, x + n)

    所以最终在同一集合中的小虫都必须是同一性别

    而如果存在 i,满足 i 和 i + n 在同一个集合中

    那么显然存在矛盾了

    #include <cstdio>
    
    int Case, n, m, f[4444];
    
    int find_(int x) {
        if(f[x] != x) return f[x] = find_(f[x]);
        return x;
    }
    
    void union_(int x, int y) {
        x = find_(x), y = find_(y);
        if(x != y) f[y] = x;
    }
    
    int main() {
        int x, y, ans = 1;
        scanf("%d", &Case);
        for(int t = 1;t <= Case;t ++) {
            ans = 1, printf("Scenario #%d:
    ", t);
            scanf("%d %d", &n, &m);
            for(int i = 1;i <= n;i ++) f[i] = i, f[i + n] = i + n; 
            for(int i = 1;i <= m;i ++) {
                scanf("%d %d", &x, &y);
                union_(x, y + n), union_(x + n, y);
            }
            for(int i = 1;i <= n;i ++)
                if(find_(i) == find_(i + n)) {
                    ans = 0;
                    break;
                }
            puts(ans ? "No suspicious bugs found!
    " : "Suspicious bugs found!
    ");
        }
        return 0;
    }
    View Code

    J.看图没看题,可能是个单调队列?

    K.太长没看

    L.对区间们排个序,然后就是个简单的线段树?

    M.树状数组查个逆序对啥的

    那个不断往后放的操作其实就那样吧

    减一下加一下它对逆序对数造成的影响就行了

    #include <cstdio>
    
    int n, a[5555], c[5555];
    
    int lowbit(int x) {
        return x & (-x);
    }
    
    void add(int i) {
        while(i <= n) c[i] ++, i += lowbit(i);
    }
    
    int ask(int i) {
        int ret = 0;
        while(i > 0) ret += c[i], i -= lowbit(i);
        return ret;
    }
    
    long long sum, ans;
    
    int main() {
        while(scanf("%d", &n) != EOF) {
            sum = 0;
            for(int i = 1;i <= n;i ++) c[i] = 0;
            for(int i = 1;i <= n;i ++)
                scanf("%d", &a[i]), a[i] ++, add(a[i]), sum += i - ask(a[i]);
            ans = sum;
            for(int i = 1;i < n;i ++) {
                sum += n - 1 - ask(a[i] - 1) * 2;
                if(sum < ans) ans = sum;
            }
            printf("%lld
    ", ans);
        }
    }
    View Code

    N.map随便做,读入有点恶心就是了

    我用的getline,因为直接cin读入string类型会忽略换行和空格

    而getline就能读入一行,读入的一行为空的话,会有s[0] = ''

    也就是读进来一个空串而不会被直接忽略这个空行

    #include <map>
    #include <iostream>
    
    using namespace std;
    
    map <string, string> p;
    
    string s1, s2, s;
    
    int main() {
        int i;
        ios::sync_with_stdio(false);
        while(getline(cin, s), s[0] != '') {
            i = 0, s1 = "", s2 = "";
            while(s[i] != ' ')  s1 += s[i ++];i ++;
            while(s[i] != '') s2 += s[i ++];
            p[s2] = s1;
        }
        while(cin >> s) {
            if(p[s] != "") cout << p[s] << endl;
            else cout << "eh
    ";
        }
        return 0;
    }
    View Code

    O.很裸的主席树,离线做有点费脑子?

    P.简单题目,自己手算循环节即可

    #include<iostream>
    
    using namespace std;
    
    int a[] = {0, 1, 4, 6, 5,  2};
    
    int main() {
        int t;
        long long n;
        cin >> t;
        while(t--) cin >> n, cout << a[n % 6] << endl;
        return 0;
    }
    View Code=

    题后话:

    poj真的很老旧了啊

    1.bits/stdc++.h 这个全能库不支持

    2.ios::sync_with_stdio(false)

    取消cin与stdio的同步都不支持

    就是加了这句话依旧是cin的速度...

    3.G++和C++两个选项很迷

    编译标准和运行时间都会有差别...

    一个编译过了另一个可能编译不过...

    一个提交超时另一个可能就通过了...

  • 相关阅读:
    第二次冲刺(7)
    第二次冲刺(6)
    第二次冲刺(5)
    【整理】C#文件操作大全(SamWang)<转>
    c# 文件及目录操作类
    BIOS设置之UEFI/Legacy BIOS切换图文详解
    UEFI+GPT引导实践篇(二):UEFI引导安装64位Win7/Win8
    UEFI+GPT引导实践篇(一):切换到UEFI启动,准备安装介质
    UEFI+GPT引导基础篇(一):什么是GPT,什么是UEFI?
    预装WIN8系统的电脑安装WIN7的方法
  • 原文地址:https://www.cnblogs.com/ytytzzz/p/6864853.html
Copyright © 2011-2022 走看看