前言
欢迎讨论,欢迎纠错,本次报告,证明从简。
7 - 1 高精度加法
解题思路
简而言之,使用数组模拟列竖式的过程,例如我们可以用 A [ i ] A[i] A[i] 表示某个数的 1 0 i 10^i 10i 位上的值。
代码实现
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int maxn = 210 + 5;
char buff[maxn];
struct bigInt {
int mem[maxn];
int len;
int flag;
void input() {
scanf("%s", buff);
len = strlen(buff);
memset(mem, 0x00, sizeof(mem));
if(buff[0] == '-') {
flag = 1; /// < 0
for(int i = 0; i < len-1; i ++) {
mem[i] = buff[len - i - 1] - '0';
}
len --;
}else {
flag = 0; /// > = 0
for(int i = 0; i < len; i ++) {
mem[i] = buff[len - i - 1] - '0';
}
if(len == 1 && mem[0] == 0) {
len --;
}
}
}
void output() {
//if(flag) putchar('-');
if(len == 0) {
printf("0");
}else {
for(int i = len - 1 ; i >= 0; i --) {
printf("%d", mem[i]);
}
}
}
};
bigInt& operator +=(bigInt& A, bigInt& B) {
A.len = max(A.len, B.len);
for(int i = 0; i < A.len; i ++) {
A.mem[i] += B.mem[i];
A.mem[i+1] += A.mem[i] / 10;
A.mem[i] %= 10;
}
if(A.mem[A.len] != 0) {
A.len ++;
}
return A;
}
bool operator>=(bigInt& A, bigInt& B) {
if(A.len != B.len) return A.len > B.len;
for(int i = A.len-1; i >= 0; i --) {
if(A.mem[i] != B.mem[i]) {
return A.mem[i] > B.mem[i];
}
}
return true; /// same
}
bigInt operator -=(bigInt& A, bigInt& B) {
for(int i = 0; i < B.len; i ++) {
A.mem[i] -= B.mem[i];
while(A.mem[i] < 0) {
A.mem[i] += 10;
A.mem[i + 1] --;
}
}
while(A.len > 0 && A.mem[A.len - 1] == 0) {
A.len --;
}
return A;
}
bigInt Ans, Bns, Tmp;
int main() {
int n; scanf("%d", &n);
for(int i = 0; i < n; i ++) {
Tmp.input();
//Tmp.output(); putchar('\n');
if(Tmp.flag) {
/// Tmp < 0
Bns += Tmp;
}else {
Ans += Tmp;
}
}
//Ans.output(); putchar('\n');
//Bns.output(); putchar('\n');
if(Ans >= Bns) { /// ans not net
Ans -= Bns;
Ans.output();
}else {
putchar('-');
Bns -= Ans;
Bns.output();
}
return 0;
}
7 - 2 二叉树加权距离
解题思路
先通过一次 dfs
找到每个节点在二叉树中的深度,为了编程简单我们记根节点的深度为
1
1
1。
代码实现
#include <cstdio>
//#include <vector>
#include <algorithm>
using namespace std;
const int maxn = 100000 + 5;
struct vector {
int mem[3], cnt;
void push_back(int x) {
mem[cnt ++] = x;
}
int size() {
return cnt;
}
int& operator[](int index) {
return mem[index];
}
};
vector nxt[maxn];
void addedge(int f, int t) {
nxt[f].push_back(t);
nxt[t].push_back(f);
}
int fa[maxn], dep[maxn];
void dfs(int x, int f) {
fa[x] = f;
dep[x] = dep[f] + 1;
for(int i = 0; i < nxt[x].size(); i ++) {
int t = nxt[x][i];
if(t == f) continue;
dfs(t, x);
}
}
int lca(int x, int y) {
if(dep[x] < dep[y]) {
swap(x, y); /// dep[x] >= dep[y]
}
while(dep[x] > dep[y]) {
x = fa[x];
}
while(x != y) {
x = fa[x];
y = fa[y];
}
return x;
}
int main() {
int n; scanf("%d", &n);
for(int i = 1; i < n; i ++) {
int a, b; scanf("%d%d", &a, &b);
addedge(a, b);
}
dfs(1, 0);
int u, v; scanf("%d%d", &u, &v);
int p = lca(u, v);
printf("%d", (dep[u] - dep[p])*3 + (dep[v] - dep[p])*2);
return 0;
}
7 - 3 修轻轨
解题思路
利用 Kruskal
的基本思想,对所有边进行排序。按照边权从小到大枚举每一条边
(
u
,
v
)
(u,v)
(u,v),并将这条边
u
u
u 与
v
v
v 当前所在的连通块在并查集上进行合并。当
1
1
1 号节点与
n
n
n 号节点第一次位于同一个连通块时,此时最后一条被考虑的边就是答案。
代码实现
#include <cstdio>
#include <algorithm>
using namespace std;
const int maxn = 100000 + 6;
int fa[maxn], bcnt;
void init(int n) {
for(int i = 1; i <= n; i ++) {
fa[i] = i;
}
bcnt = n;
}
int find(int x) {
if(x == fa[x]) return x;
else return fa[x] = find(fa[x]);
}
struct Edge {
int f, t, c;
} es[2*maxn];
bool cmp(Edge& A, Edge& B) {
return A.c < B.c;
}
void link(int x, int y) {
x = find(x);
y = find(y);
fa[x] = y;
}
int main() {
int n, m; scanf("%d%d", &n, &m);
for(int i = 1; i <= m; i ++) {
int a, b, c; scanf("%d%d%d", &a, &b, &c);
es[i] = (Edge) {a, b, c};
}
sort(es + 1, es + m + 1, cmp);
init(n);
int ans = 0;
for(int i = 1; i <= m; i ++) {
int f = es[i].f, t = es[i].t, c = es[i].c;
if(find(f) != find(t)) {
bcnt --;
link(f, t);
ans = c;
}
if(find(1) == find(n)) break;
}
printf("%d\n", ans);
return 0;
}
7 - 4 数据结构设计
解题思路 1
维护一个单调递增的单调队列以及一个单调递减的单调队列,可以 O ( 1 ) O(1) O(1) 查询当前区间的最大值和最小值。对所有数据取反可以通过一个标记实现。稍微具体解释一下“区间”的含义,将所有 I I I 操作插入到序列中的元素按照时间先后顺序排成一个数列 A A A,不难说明,每次查询最值时,“数据结构” 中的元素一定是 A A A 数列中的某个区间。
代码实现 1
#include <cstdio>
#include <algorithm>
//#include <queue>
using namespace std;
const int maxn = 400000 + 6;
struct deque {
int mem[maxn];
int L, R;
void push_back(int x) {
mem[++ R] = x;
}
void pop_front() {
if(L < R) {
L ++;
}
}
void pop_back() {
if(L < R) {
R --;
}
}
int front() {
return mem[L + 1];
}
int back() {
return mem[R];
}
bool empty() {
return L >= R;
}
};
deque UP, DOWN;
int flag = 0, A[maxn], acnt, bcnt;
void insert(int id) {
while(!UP.empty() && A[UP.back()] >= A[id]) UP.pop_back();
UP.push_back(id);
while(!DOWN.empty() && A[DOWN.back()] <= A[id]) DOWN.pop_back();
DOWN.push_back(id);
}
int MAX() {
return A[DOWN.front()];
}
int MIN() {
return A[UP.front()];
}
int main() {
int m; scanf("%d", &m);
int first = 1;
for(int i = 1; i <= m; i ++) {
char op[3]; scanf("%s", op);
if(op[0] == 'I') {
int x; scanf("%d", &x);
if(flag) {
x = -x;
}
A[++ acnt] = x; /// acnt 指向最后一个位置
insert(acnt);
}else if(op[0] == 'D') {
if(bcnt < acnt) {
bcnt ++; /// 已经被删除的部分的最后一个元素
while(!UP.empty() && UP.front() <= bcnt) UP.pop_front();
while(!DOWN.empty() && DOWN.front() <= bcnt) DOWN.pop_front();
}
}else if(op[0] == 'R') {
flag ^= 1;
}else {
/// MAX
if(bcnt < acnt) {
/// with number
if(!first) putchar('\n');
if(flag) {
printf("%d", -MIN());
}else {
printf("%d", MAX());
}
first = 0;
}
}
}
return 0;
}
解题思路 2
使用双端队列维护当前“数据结构”中的数据序列,用一个 map
记录 “数据结构” 中每种数值的出现次数 或者 multiset
维护 “数据结构” 中的数据集合。
代码实现 2
#include <cstdio>
#include <map>
#include <queue>
#include <cstring>
using namespace std;
int flag = 0; /// positive
map<int, int> cnt;
queue<int> q;
int main() {
int M; scanf("%d", &M);
int nfst = 0;
while(M --) {
char op[3]; scanf("%s", op);
if(op[0] == 'I') {
int x; scanf("%d", &x);
if(flag) {
x = - x;
}
q.push(x);
cnt[x] ++;
}else if(op[0] == 'D') {
if(!q.empty()) {
/// 删除队首
int x = q.front(); q.pop();
cnt[x] --;
if(cnt[x] == 0) {
cnt.erase(x);
}
}
}else if(op[0] == 'R') {
flag ^= 1;
}else if(op[0] == 'M') {
if(!q.empty()) {
if(nfst) {
putchar('\n');
}
if(flag) {
/// neg
printf("%d", -cnt.begin()->first);
}else {
printf("%d", cnt.rbegin()->first);
}
nfst = 1;
}
}
}
return 0;
}