题目在这里
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; }
我怎么就脑子抽了写了个极丑的封装呢
注意选择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]); } } } }
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]); } } } }
多组数据的话,基本注意每次都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(""); } }
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; }
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(); } }
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; }
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; }
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); } }
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] != '