写在前面:
最长上升子序列,一个模型居然错掉了,本题数据较大需要离散,没有对数据范围引起重视,小于等于边界没有判定清楚,细节不到位只有70分;
第二题是dfs序的应用,之前没听懂。。。:现在补上:
摘自:dfs序
DFS序 参考许昊然《数据结构漫谈》
原文
"所谓DFS序, 就是DFS整棵树依次访问到的结点组成的序列"
"DFS序有一个很强的性质: 一颗子树的所有节点在DFS序内是连续的一段, 利用这个性质我们可以解决很多问题"
基本代码 :
void Dfs(int u, int fa, int dep) { seq[++cnt] = u; st[u] = cnt; int len = edge[u].size(); for(int i = 0; i < len; i++) { int v = edge[u][i]; if(v != fa) { Dfs(v, u, dep+1); } } seq[++cnt] = u; ed[u] = cnt; }
关于原文中列出的7个经典问题, 分别实现一下
给定一颗树, 和每个节点的权值
1. 对某个节点X权值加上一个数W, 查询某个子树X里所有点权的和
解 :
由于X子树在DFS序中是连续的一段, 只需要维护一个序列,
支持单点修改和区间查询, 用树状数组就能实现
#include <cstdio> #include <cstring> #include <algorithm> #include <vector> using namespace std; const int MAXN = 1e5+10; vector<int> edge[MAXN]; int s[2*MAXN]; int seq[2*MAXN]; int st[MAXN]; int ed[MAXN]; int cnt; int Lowbit(int x) { return x & (-x); } void Add(int x, int val, int n) { for(int i = x; i <= n; i += Lowbit(i)) { s[i] += val; } } int Sum(int x) { int res = 0; for(int i = x; i > 0; i -= Lowbit(i)) { res += s[i]; } return res; } void Dfs(int u, int fa) { seq[++cnt] = u; st[u] = cnt; int len = edge[u].size(); for(int i = 0; i < len; i++) { int v = edge[u][i]; if(v != fa) { Dfs(v, u); } } seq[++cnt] = u; ed[u] = cnt; } void Init(int n) { for(int i = 0; i <= n; i++) { edge[i].clear(); } memset(s, 0, sizeof(s)); } int main() { int n, op; int u, v, w; int cmd; while(scanf("%d %d", &n, &op) != EOF) { Init(n); for(int i = 0; i < n-1; i++) { scanf("%d %d", &u, &v); edge[u].push_back(v); edge[v].push_back(u); } cnt = 0; Dfs(1, -1); while(op--) { scanf("%d", &cmd); if(cmd == 0) { scanf("%d %d", &u, &w); Add(st[u], w, cnt); } else if(cmd == 1) { scanf("%d", &u); printf("%d\n", Sum(ed[u]) - Sum(st[u] - 1)); } } } return 0; }
2. 对节点X到Y的最短路上所有点权都加一个数W, 查询某个点的权值
解 :
这个操作等价于
a. 对X到根节点路径上所有点权加W
b. 对Y到根节点路径上所有点权加W
c. 对LCA(x, y)到根节点路径上所有点权值减W
d. 对LCA(x,y)的父节点 parent(LCA(x, y))到根节点路径上所有权值减W
于是要进行四次这样从一个点到根节点的区间修改
将问题进一步简化, 进行一个点X到根节点的区间修改, 查询其他一点Y时
只有X在Y的子树内, X对Y的值才有贡献且贡献值为W
于是只需要更新四个点, 查询一个点的子树内所有点权的和即可
#include <cstdio> #include <cstring> #include <algorithm> #include <vector> using namespace std; const int MAXN = 1e5+10; vector<int> edge[MAXN]; int s[2*MAXN]; int seq[2*MAXN]; int seq1[2*MAXN]; int depth[2*MAXN]; int first[MAXN]; int dp[2*MAXN][25]; int st[MAXN]; int ed[MAXN]; int parent[MAXN]; int cnt, num; int Lowbit(int x) { return x & (-x); } void Add(int x, int val, int n) { if(x <= 0) return; for(int i = x; i <= n; i += Lowbit(i)) { s[i] += val; } } int Sum(int x) { int res = 0; for(int i = x; i > 0; i -= Lowbit(i)) { res += s[i]; } return res; } void Dfs(int u, int fa, int dep) { parent[u] = fa; seq[++cnt] = u; seq1[++num] = u; first[u] = num; depth[num] = dep; st[u] = cnt; int len = edge[u].size(); for(int i = 0; i < len; i++) { int v = edge[u][i]; if(v != fa) { Dfs(v, u, dep+1); seq1[++num] = u; depth[num] = dep; } } seq[++cnt] = u; ed[u] = cnt; } void RMQ_Init(int n) { for(int i = 1; i <= n; i++) { dp[i][0] = i; } for(int j = 1; (1 << j) <= n; j++) { for(int i = 1; i + (1 << j) - 1 <= n; i++) { int a = dp[i][j-1], b = dp[i + (1 << (j-1))][j-1]; dp[i][j] = depth[a] < depth[b] ? a : b; } } } int RMQ_Query(int l, int r) { int k = 0; while((1 << (k + 1)) <= r - l + 1) k++; int a = dp[l][k], b = dp[r-(1<<k)+1][k]; return depth[a] < depth[b] ? a : b; } int LCA(int u, int v) { int a = first[u], b = first[v]; if(a > b) a ^= b, b ^= a, a ^= b; int res = RMQ_Query(a, b); return seq1[res]; } void Init(int n) { for(int i = 0; i <= n; i++) { edge[i].clear(); } memset(s, 0, sizeof(s)); } int main() { int n, op; int u, v, w; int cmd; while(scanf("%d %d", &n, &op) != EOF) { Init(n); for(int i = 0; i < n-1; i++) { scanf("%d %d", &u, &v); edge[u].push_back(v); edge[v].push_back(u); } cnt = 0, num = 0; Dfs(1, -1, 0); RMQ_Init(num); while(op--) { scanf("%d", &cmd); if(cmd == 0) { scanf("%d %d %d", &u, &v, &w); int lca = LCA(u, v); Add(st[u], w, cnt); Add(st[v], w, cnt); Add(lca, -w, cnt); Add(parent[lca], -w, cnt); } else if(cmd == 1) { scanf("%d", &u); printf("%d\n", Sum(ed[u]) - Sum(st[u] - 1)); } } } return 0; }
3. 对节点X到Y的最短路上所有点权都加一个数W, 查询某个点子树的权值之和
解 :
同问题2中的修改方法, 转化为修改某点到根节点的权值加/减W
当修改某个节点A, 查询另一节点B时
只有A在B的子树内, Y的值会增加 W * (depth[A] - depth[B] + 1) == W * (depth[A] + 1) - W * depth[B]
同样是用树状数组来查询Y的子树, 修改 和 查询方法都要新增一个数组
第一个数组保存 W * (depth[A] + 1), 第二个数组保存 W
每次查询结果为Sum(ed[B]) - Sum(st[B]-1) - (Sum1(ed[B]) - Sum(st[B]-1)) * depth[B]
#include <cstdio> #include <cstring> #include <algorithm> #include <vector> using namespace std; const int MAXN = 1e5+10; vector<int> edge[MAXN]; int s[2*MAXN]; int s1[2*MAXN]; int seq[2*MAXN]; int seq1[2*MAXN]; int depth[2*MAXN]; int first[MAXN]; int dp[2*MAXN][25]; int st[MAXN]; int ed[MAXN]; int parent[MAXN]; int cnt, num; int Lowbit(int x) { return x & (-x); } void Add(int x, int val, int n) { if(x <= 0) return; for(int i = x; i <= n; i += Lowbit(i)) { s[i] += val; } } void Add1(int x, int val, int n) { if(x <= 0) return; for(int i = x; i <= n; i += Lowbit(i)) { s1[i] += val; } } int Sum(int x) { int res = 0; for(int i = x; i > 0; i -= Lowbit(i)) { res += s[i]; } return res; } int Sum1(int x) { int res = 0; for(int i = x; i > 0; i -= Lowbit(i)) { res += s1[i]; } return res; } void Dfs(int u, int fa, int dep) { parent[u] = fa; seq[++cnt] = u; seq1[++num] = u; first[u] = num; depth[num] = dep; st[u] = cnt; int len = edge[u].size(); for(int i = 0; i < len; i++) { int v = edge[u][i]; if(v != fa) { Dfs(v, u, dep+1); seq1[++num] = u; depth[num] = dep; } } seq[++cnt] = u; ed[u] = cnt; } void RMQ_Init(int n) { for(int i = 1; i <= n; i++) { dp[i][0] = i; } for(int j = 1; (1 << j) <= n; j++) { for(int i = 1; i + (1 << j) - 1 <= n; i++) { int a = dp[i][j-1], b = dp[i + (1 << (j-1))][j-1]; dp[i][j] = depth[a] < depth[b] ? a : b; } } } int RMQ_Query(int l, int r) { int k = 0; while((1 << (k + 1)) <= r - l + 1) k++; int a = dp[l][k], b = dp[r-(1<<k)+1][k]; return depth[a] < depth[b] ? a : b; } int LCA(int u, int v) { int a = first[u], b = first[v]; if(a > b) a ^= b, b ^= a, a ^= b; int res = RMQ_Query(a, b); return seq1[res]; } void Init(int n) { for(int i = 0; i <= n; i++) { edge[i].clear(); } memset(s, 0, sizeof(s)); } int main() { int n, op; int u, v, w; int cmd; while(scanf("%d %d", &n, &op) != EOF) { Init(n); for(int i = 0; i < n-1; i++) { scanf("%d %d", &u, &v); edge[u].push_back(v); edge[v].push_back(u); } cnt = 0, num = 0; Dfs(1, -1, 0); RMQ_Init(num); while(op--) { scanf("%d", &cmd); if(cmd == 0) { scanf("%d %d %d", &u, &v, &w); int lca = LCA(u, v); Add(st[u], w * depth[first[u]] + w, cnt); Add1(st[u], w, cnt); Add(st[v], w * depth[first[v]] + w, cnt); Add1(st[v], w, cnt); Add(lca, -(w * depth[first[lca]] + w), cnt); Add1(lca, -w, cnt); Add(parent[lca], -(w * depth[first[parent[lca]]] + w), cnt); Add1(parent[lca], -w, cnt); } else if(cmd == 1) { scanf("%d", &u); printf("%d\n", Sum(ed[u]) - Sum(st[u] - 1) - \ depth[first[u]] * (Sum1(ed[u]) - Sum1(st[u] - 1))); } } } return 0; }
4. 对某个点X权值加上一个数W, 查询X到Y路径上所有点权之和
解 :
求X到Y路径上所有的点权之和, 和前面X到Y路径上所有点权加一个数相似
这个问题转化为
X到根节点的和 + Y到根节点的和 - LCA(x, y)到根节点的和 - parent(LCA(x,y)) 到根节点的和
于是只要支持单点修改, 区间查询即可
要注意的是修改, 要在该点开始出现位置加W, 在结束位置减W
这样能保证在该节点子树内的能加到这个W, 不在该点子树内的无影响
#include <cstdio> #include <cstring> #include <algorithm> #include <vector> using namespace std; const int MAXN = 1e5+10; vector<int> edge[MAXN]; int s[2*MAXN]; int s1[2*MAXN]; int seq[2*MAXN]; int seq1[2*MAXN]; int depth[2*MAXN]; int first[MAXN]; int dp[2*MAXN][25]; int st[MAXN]; int ed[MAXN]; int parent[MAXN]; int cnt, num; int Lowbit(int x) { return x & (-x); } void Add(int x, int val, int n) { if(x <= 0) return; for(int i = x; i <= n; i += Lowbit(i)) { s[i] += val; } } int Sum(int x) { int res = 0; for(int i = x; i > 0; i -= Lowbit(i)) { res += s[i]; } return res; } void Dfs(int u, int fa, int dep) { parent[u] = fa; seq[++cnt] = u; seq1[++num] = u; first[u] = num; depth[num] = dep; st[u] = cnt; int len = edge[u].size(); for(int i = 0; i < len; i++) { int v = edge[u][i]; if(v != fa) { Dfs(v, u, dep+1); seq1[++num] = u; depth[num] = dep; } } seq[++cnt] = u; ed[u] = cnt; } void RMQ_Init(int n) { for(int i = 1; i <= n; i++) { dp[i][0] = i; } for(int j = 1; (1 << j) <= n; j++) { for(int i = 1; i + (1 << j) - 1 <= n; i++) { int a = dp[i][j-1], b = dp[i + (1 << (j-1))][j-1]; dp[i][j] = depth[a] < depth[b] ? a : b; } } } int RMQ_Query(int l, int r) { int k = 0; while((1 << (k + 1)) <= r - l + 1) k++; int a = dp[l][k], b = dp[r-(1<<k)+1][k]; return depth[a] < depth[b] ? a : b; } int LCA(int u, int v) { int a = first[u], b = first[v]; if(a > b) a ^= b, b ^= a, a ^= b; int res = RMQ_Query(a, b); return seq1[res]; } void Init(int n) { for(int i = 0; i <= n; i++) { edge[i].clear(); } memset(s, 0, sizeof(s)); } int main() { int n, op; int u, v, w; int cmd; while(scanf("%d %d", &n, &op) != EOF) { Init(n); for(int i = 0; i < n-1; i++) { scanf("%d %d", &u, &v); edge[u].push_back(v); edge[v].push_back(u); } cnt = 0, num = 0; Dfs(1, -1, 0); RMQ_Init(num); while(op--) { scanf("%d", &cmd); if(cmd == 0) { scanf("%d %d", &u, &w); Add(st[u], w, cnt); Add(ed[u], -w, cnt); } else if(cmd == 1) { scanf("%d %d", &u, &v); int lca = LCA(u, v); printf("%d\n", Sum(st[u]) + Sum(st[v]) - Sum(st[lca]) - Sum(st[parent[lca]])); } } } return 0; }
5. 对子树X里所有节点加上一个值W, 查询某个点的值
解 :
对DFS序来说, 子树内所有节点加W, 就是一段区间加W
所以这个问题就是 区间修改, 单点查询
#include <cstdio> #include <cstring> #include <algorithm> #include <vector> using namespace std; const int MAXN = 1e5+10; vector<int> edge[MAXN]; int s[2*MAXN]; int s1[2*MAXN]; int seq[2*MAXN]; int seq1[2*MAXN]; int depth[2*MAXN]; int first[MAXN]; int dp[2*MAXN][25]; int st[MAXN]; int ed[MAXN]; int parent[MAXN]; int cnt, num; int Lowbit(int x) { return x & (-x); } void Add(int x, int val, int n) { if(x <= 0) return; for(int i = x; i <= n; i += Lowbit(i)) { s[i] += val; } } int Sum(int x) { int res = 0; for(int i = x; i > 0; i -= Lowbit(i)) { res += s[i]; } return res; } void Dfs(int u, int fa) { seq[++cnt] = u; st[u] = cnt; int len = edge[u].size(); for(int i = 0; i < len; i++) { int v = edge[u][i]; if(v != fa) { Dfs(v, u); } } seq[++cnt] = u; ed[u] = cnt; } void Init(int n) { for(int i = 0; i <= n; i++) { edge[i].clear(); } memset(s, 0, sizeof(s)); } int main() { int n, op; int u, v, w; int cmd; while(scanf("%d %d", &n, &op) != EOF) { Init(n); for(int i = 0; i < n-1; i++) { scanf("%d %d", &u, &v); edge[u].push_back(v); edge[v].push_back(u); } cnt = 0; Dfs(1, -1); while(op--) { scanf("%d", &cmd); if(cmd == 0) { scanf("%d %d", &u, &w); Add(st[u], w, cnt); Add(ed[u], -w, cnt); } else if(cmd == 1) { scanf("%d", &u); printf("%d\n", Sum(u)); } } } return 0; }
6.对子树X里所有节点加上一个值W, 查询某个子树的权值和
解 :
子树所有节点加W, 就是某段区间加W, 查询某个子树的权值和, 就是查询某段区间的和
区间修改区间求和, 是线段树的经典问题, 用线段树可以很好解决
#include <cstdio> #include <cstring> #include <algorithm> #include <vector> using namespace std; typedef struct { int l, r, sum, add; } Seg; const int MAXN = 1e5+10; Seg T[4*MAXN]; vector<int> edge[MAXN]; int s[2*MAXN]; int seq[2*MAXN]; int st[MAXN]; int ed[MAXN]; int cnt; void Dfs(int u, int fa) { seq[++cnt] = u; st[u] = cnt; int len = edge[u].size(); for(int i = 0; i < len; i++) { int v = edge[u][i]; if(v != fa) { Dfs(v, u); } } seq[++cnt] = u; ed[u] = cnt; } void Build(int l, int r, int k) { T[k].l = l, T[k].r = r, T[k].sum = T[k].add; if(l == r) return; int mid = (l + r) >> 1; Build(l, mid, k<<1); Build(mid+1, r, k<<1|1); } void PushDown(int k) { if(T[k].add) { T[k<<1].sum += (T[k<<1].r - T[k<<1].l + 1) * T[k].add; T[k<<1].add += T[k].add; T[k<<1|1].sum += (T[k<<1|1].r - T[k<<1|1].l + 1) * T[k].add; T[k<<1|1].add += T[k<<1].add; T[k].add = 0; } } void PushUp(int k) { T[k].sum = T[k<<1].sum + T[k<<1|1].sum; } void Update(int l, int r, int val, int k) { if(T[k].l == l && T[k].r == r) { T[k].sum += (r - l + 1) * val; T[k].add += val; return ; } PushDown(k); int mid = (T[k].l + T[k].r) >> 1; if(r <= mid) Update(l, r, val, k<<1); else if(l > mid) Update(l, r, val, k<<1|1); else { Update(l, mid, val, k<<1); Update(mid+1, r, val, k<<1|1); } PushUp(k); } int Query(int l, int r, int k) { if(T[k].l == l && T[k].r == r) { return T[k].sum; } PushDown(k); int mid = (T[k].l + T[k].r) >> 1; if(r <= mid) return Query(l, r, k<<1); else if(l > mid) return Query(l, r, k<<1|1); else { return Query(l, mid, k<<1) + Query(mid+1, r, k<<1|1); } } void Init(int n) { for(int i = 0; i <= n; i++) { edge[i].clear(); } } int main() { int n, op; int u, v, w; int cmd; while(scanf("%d %d", &n, &op) != EOF) { Init(n); for(int i = 0; i < n-1; i++) { scanf("%d %d", &u, &v); edge[u].push_back(v); edge[v].push_back(u); } cnt = 0; Dfs(1, -1); Build(1, cnt, 1); while(op--) { scanf("%d", &cmd); if(cmd == 0) { scanf("%d %d", &u, &w); Update(st[u], ed[u], w, 1); } else if(cmd == 1) { scanf("%d", &u); printf("%d\n", Query(st[u], ed[u], 1) / 2); } } } return 0; }
7. 对节点X的子树所有节点加上一个值W, 查询X到Y的路径上所有点的权值和
解:
同问题4把路径上求和转化为四个点到根节点的和
X到根节点的和 + Y到根节点的和 - LCA(x, y)到根节点的和 - parent(LCA(x,y)) 到根节点的和
修改一点A, 查询某点B到根节点时, 只有B在A的子树内, A对B才有贡献
贡献为W * (depth[B] - depth[A] + 1) == W * (1 - depth[A]) + W * depth[B]
和第三题一样, 用两个树状数组维护 W *(depth[A] + 1), W * depth[B]
同样要注意修改某点时, 在一点开始位置加, 在其结束位置减
#include <cstdio> #include <cstring> #include <algorithm> #include <vector> using namespace std; typedef struct { int l, r, sum, add; } Seg; const int MAXN = 1e5+10; Seg T[4*MAXN]; vector<int> edge[MAXN]; int s[2*MAXN]; int s1[2*MAXN]; int seq[2*MAXN]; int seq1[2*MAXN]; int depth[2*MAXN]; int first[MAXN]; int dp[2*MAXN][25]; int parent[MAXN]; int st[MAXN]; int ed[MAXN]; int cnt, cnt1; int Lowbit(int x) { return x & (-x); } void Add(int x, int val, int n) { if(x <= 0) return ; for(int i = x; i <= n; i += Lowbit(i)) { s[i] += val; } } void Add1(int x, int val, int n) { if(x <= 0) return ; for(int i = x; i <= n; i += Lowbit(i)) { s1[i] += val; } } int Sum(int x) { int res = 0; for(int i = x; i > 0; i -= Lowbit(i)) { res += s[i]; } return res; } int Sum1(int x) { int res = 0; for(int i = x; i > 0; i -= Lowbit(i)) { res += s1[i]; } return res; } void RMQ_Init(int n) { for(int i = 1; i <= n; i++) { dp[i][0] = i; } for(int j = 1; (1 << j) <= n; j++) { for(int i = 1; i + (1 << j) - 1 <= n; i++) { int a = dp[i][j-1], b = dp[i + (1 << (j-1))][j-1]; dp[i][j] = depth[a] < depth[b] ? a : b; } } } int RMQ_Query(int l, int r) { int k = 0; while((1 << (k + 1)) <= r - l + 1) k++; int a = dp[l][k], b = dp[r-(1 << k)+1][k]; return depth[a] < depth[b] ? a : b; } int LCA(int u, int v) { int a = first[u], b = first[v]; if(a > b) a ^= b, b ^= a, a ^= b; int res = RMQ_Query(a, b); return seq1[res]; } void Dfs(int u, int fa, int dep) { seq[++cnt] = u; seq1[++cnt1] = u; first[u] = cnt1; parent[u] = fa; depth[cnt1] = dep; st[u] = cnt; int len = edge[u].size(); for(int i = 0; i < len; i++) { int v = edge[u][i]; if(v != fa) { Dfs(v, u, dep+1); seq1[++cnt1] = u; depth[cnt1] = dep; } } seq[++cnt] = u; ed[u] = cnt; } void Init(int n) { for(int i = 0; i <= n; i++) { edge[i].clear(); } memset(s, 0, sizeof(s)); memset(s1, 0, sizeof(s1)); } void Debug() { int u, v; while(1) { scanf("%d %d", &u, &v); printf("The LCA of %d %d is %d\n", u, v, LCA(u, v)); } } int main() { int n, op; int u, v, w; int cmd; while(scanf("%d %d", &n, &op) != EOF) { Init(n); for(int i = 0; i < n-1; i++) { scanf("%d %d", &u, &v); edge[u].push_back(v); edge[v].push_back(u); } cnt = cnt1 = 0; Dfs(1, 0, 0); RMQ_Init(cnt1); while(op--) { scanf("%d", &cmd); if(cmd == 0) { scanf("%d %d", &u, &w); Add(st[u], w * (1 - depth[first[u]]), cnt); Add(ed[u], -w * (1 - depth[first[u]]), cnt); Add1(st[u], w, cnt); Add1(ed[u], -w, cnt); } else if(cmd == 1) { scanf("%d %d", &u, &v); int lca = LCA(u, v); int par = parent[lca]; int ans = Sum(st[u]); ans += depth[first[u]] * Sum1(st[u]); ans += Sum(st[v]); ans += depth[first[v]] * Sum1(st[v]); ans -= Sum(st[lca]); ans -= depth[first[lca]] * Sum1(st[lca]); ans -= Sum(st[par]); ans -= depth[first[par]] * Sum1(st[par]); printf("%d\n", ans); } } } return 0; }
第三题,n方算法,常数优化太弱。(根本没有),只有60。。。
总:130/300。好菜啊啊啊,一半的分都没拿到
附上题目
A
题目描述
给出一个长度为 n 的序列 A,求这个序列的最长上升子序列的长度。
输入格式
第一行一个正整数 n。
第二行 n 个正整数,第 i 个正整数表示 A[i]。
输出格式
一个正整数,即最长上升子序列的长度。
样例输入
5
14532
样例输出
3
数据范围和提示
对于 30%的数据:n<=1e3,A[i]<=1e9
另有 30%的数据:n<=1e5,A[i]<=1e5
对于 100%的数据:n<=1e5,A[i]<=1e9
所有数字均为正整数。
考试代码:没加=,
#include<cstdio>
#include<iostream>
using namespace std;
int n,a[100005];
int ans[100005];
int m=0;
void div(int l,int r,int x)
{
while(l!=r)
{
int mid=(l+r)/2;
if(ans[mid]=>x)r=mid;//
else l=mid+1;
}
ans[l]=x;
return ;
}
int main()
{ freopen("A.in","r",stdin);freopen("A.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
//cout<<n<<endl;
for(int i=1;i<=n;i++)
{
if(a[i]>ans[m])++m,ans[m]=a[i];
else
div(1,m,a[i]);
}//cout<<a[n]<<endl;
printf("%d",m);
fclose(stdin);fclose(stdout);
return 0;
}
B
题目描述
给出一棵大小为 n 的树,根为 1。点有点权。给出 m 个操作或询问:
1. 操作:节点 p 权值增加 w。
2. 询问:两点(u,v)的路径点权和。
输入格式
第一行一个正整数 n。
第二行 n-1 个正整数,表示节点 2 到节点 n 的父亲节点。
第三行 n 个正整数,表示节点 1 到节点 n 的点权 val。
第三行一个正整数 m。
接下来 m 行,每行三个正整数 opt,x,y,若 opt=1 表示将节点 x 的权值增加 y,若 opt=2 表示
查询节点 x 与节点 y 路径的点权和。
输出格式
输出包括若干行,对于每一个询问,输出点权和。
样例输入
10
111355342
1 2 3 4 5 6 7 8 9 10
5
286
259
137278
297
样例输出
22
22
30
36
数据范围
对于 30%的数据:n<=1e3
另有 20%的数据:opt=1 的情况只有 1 次。
另有 20%的数据:opt=2 的情况只有 1 次。
对于 100%的数据:n<=1e5
所有数字均为正整数,且<=1e9
C
题目描述
给出一个长度为 n 的排列 A,求这个排列有多少个长度为奇数的子串中位数是 mid。
输入格式
第一行两个正整数 n 与 mid。
第二行 n 个正整数,第 i 个数表示 A[i]。
输出格式
一个正整数,即答案。
样例输入
54
15423
样例输出
3
样例解释
这三个子串分别为[1 5 4][5 4 2][4]
数据范围
对于 30%的数据:n<=2e2
对于 60%的数据:n<=2e3
对于 100%的数据:n<=1e6,mid<=n
所有数字均为正整数。