复健训练难题扫除计划
写在最前
本次没有专门设置签到题, 为了降低一定的难度, 作者将题目思路写在了题目标题处
- 预期结果:
8/13
- 理想结果:
11/13
- 实际结果:
10/13
B - 搜索
难点: TLE
需要多种剪枝:
- 同等长度的棍子在搜索过一次时, 必不会再被搜索
- 最终结果应不短于最长的棍子, 且不大于长度总和, 并且一定为总长度的某一约数
- 应该先搜索较长的棍子, 这样搜索得到的搜索树较小
c - 贪心
难点: 思维
正常情况应以岛屿的X轴坐标为依据进行贪心, 但这样会导致错误结果
正确做法
:
- 以该岛屿所能涉及到的最右边的雷达为依据, 依次从小到大
G - 最近公共祖先
难点: TLE, RE, WA, MLE, CE
尤其要注意的是, 本题两点间的距离为树上路径中所有节点点权的异或和
剩下的, 就是一个基础的LCA问题, 不会还不赶快去学
代码
#include <iostream>
#include <cstring>
#include <queue>
using namespace std;
const int N = 1e5 + 10, M = 2e5 + 10;
int n, m;
int c[N];
int h[N], e[M], ne[M], idx;
int dist[N], depth[N];
int fa[N][16];
void add(int a, int b){
e[idx] = b;
ne[idx] = h[a];
h[a] = idx ++;
}
void dfs(int u, int fa){
for(int i = h[u]; i != -1; i = ne[i]){
int j = e[i];
if(j == fa) continue;
dist[j] ^= dist[u];
dfs(j, u);
}
}
void bfs(int u){
memset(depth, 0x3f, sizeof depth);
depth[0] = 0;
depth[u] = 1;
queue<int> q;
q.push(u);
while(q.size()){
int t = q.front();
q.pop();
for(int i = h[t]; i != -1; i = ne[i]){
int j = e[i];
if(depth[j] > depth[t] + 1){
depth[j] = depth[t] + 1;
q.push(j);
fa[j][0] = t;
for(int k = 1; k <= 15; k ++)
fa[j][k] = fa[fa[j][k - 1]][k - 1];
}
}
}
}
int lca(int a, int b){
if(depth[a] < depth[b]) return lca(b, a);
for(int k = 15; k >= 0; k --)
if(depth[fa[a][k]] >= depth[b])
a = fa[a][k];
if(a == b) return a;
for(int k = 15; k >= 0; k --)
if(fa[a][k] != fa[b][k]){
a = fa[a][k];
b = fa[b][k];
}
return fa[a][0];
}
int main(){
memset(h, -1, sizeof h);
cin >> n >> m;
for (int i = 1; i <= n; i ++ ) cin >> c[i], dist[i] = c[i];
for (int i = 1; i < n; i ++){
int a, b;
cin >> a >> b;
add(a, b), add(b, a);
}
dfs(1, -1);
bfs(1);
for (int i = 1; i <= m; i ++ ) {
int a, b;
cin >> a >> b;
int p = lca(a, b);
cout << (dist[a] ^ dist[b] ^ c[p]) << endl;
}
return 0;
}
I - 矩阵快速幂
难点: 无
\(\begin{pmatrix} 1 & 1 \\ 1 & 0 \end{pmatrix} \cdot \binom{f_n}{f_{n-1}} = \binom{f_{n+1}}{f_{n}}\)
同理会有扩展, 这类题目就需要自己去积累了
M - AK!!!
思路
难点: TLE, RE, WA, CE
大型的线段树, 二阶线段树
需要用到区间求和
和区间修改
, 用到的知识点为: 懒标记
代码
#include <iostream>
#include <cstdio>
using namespace std;
typedef long long LL;
const int N = 1e5 + 10;
int n, m;
LL a[N];
struct Node {
int l, r;
LL sum, add;
}tr[N * 4];
void pushup(Node& u, Node& l, Node& r){
u.sum = l.sum + r.sum;
}
void pushup(int u){
pushup(tr[u], tr[u << 1], tr[u << 1 | 1]);
}
void pushdown(Node& u, Node& l, Node& r){
if(u.add){
l.add += u.add, l.sum += (LL)(l.r - l.l + 1ll) * u.add;
r.add += u.add, r.sum += (LL)(r.r - r.l + 1ll) * u.add;
u.add = 0;
}
}
void pushdown(int u) {
pushdown(tr[u], tr[u << 1], tr[u << 1 | 1]);
}
void build(int u, int l, int r) {
if(l == r) {
tr[u].l = l;
tr[u].r = r;
tr[u].sum = a[r];
tr[u].add = 0;
// tr[u] = { l, r, a[r], 0 };
return ;
}
int mid = (l + r) >> 1;
//tr[u] = { l, r };
tr[u].l = l;
tr[u].r = r;
build(u << 1, l, mid);
build(u << 1 | 1, mid + 1, r);
pushup(u);
}
void modify(int u, int l, int r, LL c){
if(tr[u].l >= l && tr[u].r <= r){
tr[u].sum += (tr[u].r - tr[u].l + 1ll) * c;
tr[u].add += c;
return ;
}
pushdown(u);
int mid = (tr[u].l + tr[u].r) >> 1;
if(l <= mid) modify(u << 1, l, r, c);
if(r > mid) modify(u << 1 | 1, l, r, c);
pushup(u);
}
LL query(int u, int l, int r){
if(tr[u].l >= l && tr[u].r <= r) return tr[u].sum;
pushdown(u);
int mid = (tr[u].l + tr[u].r) >> 1;
LL sum = 0;
if(l <= mid) sum += query(u << 1, l, r);
if(r > mid) sum += query(u << 1 | 1, l, r);
return sum;
}
int main(){
scanf("%d%d", &n, & m);
for (int i = 1; i <= n; i ++ ) scanf("%lld", a + i);
build(1, 1, n);
while(m --){
char op[5];
scanf("%s", &op);
if(op[0] == 'Q'){
int l, r;
scanf("%d%d", &l, &r);
cout << query(1, l, r) << endl;
}
else {
int l, r, d;
scanf("%d%d%d", &l, &r, &d);
modify(1, l, r, d);
}
}
}