1.13 树状数组专练总结
wtclwtcl!!!
T3卡了一个半小时写不出来,结果听评讲是个逆序对(???)
T1 NKOJ4866 和谐数
题目大意:一个长度为n的序列,对于第i个数字,设左边比它大的有x个,右边比它大的有y个,若max(x, y) > 2 * min(x, y),则这个数不和谐。求数列中不和谐数字的总数。
解法:正序一遍树状数组,倒序一遍树状数组即可。
#include <bits/stdc++.h> using namespace std; int a[100005], b[100005], c[100005], ans1[100005], ans2[100005], n; void modify(int x, int p) { for (int i = x; i <= n; i += (i & -i)) c[i] += p; } int query(int x) { int sum = 0; for (int i = x; i; i -= (i & -i)) sum += c[i]; return sum; } int main() { scanf("%d", &n); for (int i = 1; i <= n; i++) { scanf("%d", &a[i]); b[i] = a[i]; } sort(b + 1, b + n + 1); int tot = unique(b + 1, b + n + 1) - b - 1; for (int i = 1; i <= n; i++) { a[i] = lower_bound(b + 1, b + tot + 1, a[i]) - b; } for (int i = 1; i <= n; i++) { ans1[i] = i - query(a[i]) - 1; modify(a[i], 1); } memset(c, 0, sizeof(c)); for (int i = n; i; i--) { ans2[i] = n - i - query(a[i]); modify(a[i], 1); } int sum = 0; for (int i = 1; i <= n; i++) { if (max(ans1[i], ans2[i]) > 2 * min(ans1[i], ans2[i])) sum++; } printf("%d", sum); }
T2 NKOJ4224 矩阵计数
题目大意:二维矩阵,单点修改,区间查询,强制在线。
题解:裸的二维树状数组。
#include <bits/stdc++.h> using namespace std; int a[305][305], c[305][305][105], n, m; void modify(int x, int y, int k, int p) { for (int i = x; i <= n; i += (i & -i)) { for (int j = y; j <= m; j += (j & -j)) { c[i][j][k] += p; } } } int query(int x, int y, int k) { int sum = 0; for (int i = x; i; i -= (i & -i)) { for (int j = y; j; j -= (j & -j)) { sum += c[i][j][k]; } } return sum; } int main() { scanf("%d%d", &n, &m); for (int i = 1; i <= n; i++) { for (int j = 1; j <= m; j++) { scanf("%d", &a[i][j]); modify(i, j, a[i][j], 1); } } int q; scanf("%d", &q); for (int i = 1, type, k; i <= q; i++) { scanf("%d", &type); if (type == 1) { int x, y; scanf("%d%d%d", &x, &y, &k); modify(x, y, a[x][y], -1); a[x][y] = k; modify(x, y, a[x][y], 1); } else { int x1, x2, y1, y2; scanf("%d%d%d%d%d", &x1, &y1, &x2, &y2, &k); printf("%d\n", query(x2, y2, k) - query(x2, y1 - 1, k) - query(x1 - 1, y2, k) + query(x1 - 1, y1 - 1, k)); } } }
T3(订正) NKOJ4223 彩色方块
题面:何老板最近在玩一款叫“彩色方块”的小游戏,游戏虽然简单,但何老板仍旧乐此不疲。
游戏中有n个彩色方块成一排,方块的颜色用字母表示,给出目标排列,只要把它们排成跟目标一样的排列,就算过关。每次操作只能交换相邻两个方块。
给出一关游戏,何老板想知道,最少操作几次就能过关,请你帮他计算最少所需的操作步数。
解法:用26个队列给字母编号,然后求逆序对即可。
#include <bits/stdc++.h> using namespace std; #define ll long long const ll maxn = 1000005; queue <ll> Q[120]; ll a[maxn], b[maxn], c[maxn], d[maxn], mark[maxn], nxt[maxn], head[maxn], n, ans; #define lowbit(i) i & (-i) void Modify(ll x, ll k) { for (ll i = x; i <= n; i += lowbit(i)) b[i] += k; } ll querysum(ll x) { ll ans = 0; for (ll i = x; i; i -= lowbit(i)) ans += b[i]; return ans; } ll read() { char ch = getchar(); while (ch < 'A' || ch > 'Z') ch = getchar(); return (ll) ch - 'A' + 1; } main() { scanf("%lld", &n); for (ll i = 1; i <= n; i++) c[i] = read(); for (ll i = 1, x; i <= n; i++) x = read(), Q[x].push(i); for (ll i = 1; i <= n; i++) { d[Q[c[i]].front()] = i; Q[c[i]].pop(); } for (ll i = 1; i <= n; i++) { ans += querysum(n) - querysum(d[i]); Modify(d[i], 1); } printf("%lld", ans); return 0; }
T4:NKOJ4875 前缀前缀和
题目大意:单点修改,求前缀和的前缀和。
题解:就类似树状数组区间修改,区间查询的方式维护即可。
#include <bits/stdc++.h> using namespace std; #define ll long long ll a[100005], c[100005], n, m, d1[100005], d2[100005]; #define lowbit(i) i & (-i) void modify(ll x, ll k) { for (ll i = x; i <= n; i += lowbit(i)) d1[i] += k, d2[i] += 1LL * k * (x - 1); } ll querysum(ll x) { ll sum1 = 0, sum2 = 0; for (ll i = x; i; i -= lowbit(i)) { sum1 += 1LL * d1[i] * x; sum2 += d2[i]; } return sum1 - sum2; } main() { scanf("%lld%lld", &n, &m); for (ll i = 1, x; i <= n; i++) { scanf("%lld", &a[i]); modify(i, a[i]); } string ch; while (m--) { ll i; cin >> ch; if (ch[0] == 'Q') { scanf("%lld", &i); ll sum = 0; printf("%lld\n", querysum(i)); } else { ll i, d; scanf("%lld%lld", &i, &d); modify(i, d - a[i]); a[i] = d; } } }

浙公网安备 33010602011771号