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++两个选项很迷

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

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

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

原文地址:https://www.cnblogs.com/ytytzzz/p/6864853.html