如果是初学者建议先看看这篇博客,写的很不错传送门
目录
POJ 3468 A Simple Problem with Integers(线段树区间修改+求和)
HDU 1540 Tunnel Warfare(最长连续区间+单点修改)
洛谷 P1198 [JSOI2008]最大数(线段树或RMQ)
2018年全国多校算法寒假训练营练习比赛(第五场)逆序数(树状数组)
2018年全国多校算法寒假训练营练习比赛(第五场)H Tree Recovery(线段树)
power oj 2821: 小Y学长的GCD难题(线段树区间求最大公因数+区间修改)
POJ2299 Ultra-QuickSort(树状数组求逆序数)
HDU 1166 敌兵布阵(线段树)
题解:线段树的模板题,但是用树状数组更简单一点,这里前面的这个树状数组的代码,后面的是线段树的代码。
树状数组:
#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<stack>
#include<string>
const int maxn = 5e4 + 1000;
const int mod = 1e9 + 7;
const int inf = 1e9;
#define me(a, b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
int bit[maxn];
int lowbit(int x) {
return x & (-x);
}
void update(int x, int c) {
while (x < maxn) {
bit[x] += c;
x += lowbit(x);
}
}
int qeroy(int x) {
int sum = 0;
while (x) {
sum += bit[x];
x -= lowbit(x);
}
return sum;
}
int main() {
int t;
cin >> t;
for (int tt = 1; tt <= t; tt++) {
cout << "Case " << tt << ":" << endl;
me(bit, 0);
char s[10];
int n, a, b, x;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &x);
update(i, x);
}
while (scanf("%s", s) != EOF) {
if (s[0] == 'E')
break;
scanf("%d%d", &a, &b);
if (s[0] == 'Q')
printf("%d\n", qeroy(b) - qeroy(a - 1));
else if (s[0] == 'A')
update(a, b);
else
update(a, -b);
}
}
return 0;
}
线段树:
#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<stack>
#include<string>
const int maxn = 5e4 + 5;
const int mod = 1e9 + 7;
const int inf = 1e9;
#define me(a, b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
int sum[maxn << 2];
void build(int l, int r, int rt) {
if (l == r) {
scanf("%d", &sum[rt]);
return;
}
int mid = (l + r) >> 1;
build(l, mid, rt << 1);
build(mid + 1, r, rt << 1 | 1);
sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
int num(int L, int R, int l, int r, int rt) {
if (l >= L && R >= r)
return sum[rt];
int m = (l + r) >> 1;
int s = 0;
if (L <= m)
s += num(L, R, l, m, rt << 1);
if (R > m)
s += num(L, R, m + 1, r, rt << 1 | 1);
return s;
}
void update(int s, int c, int l, int r, int rt) {
if (l == r) {
sum[rt] += c;
return;
}
int m = (l + r) >> 1;
if (s <= m)
update(s, c, l, m, rt << 1);
else
update(s, c, m + 1, r, rt << 1 | 1);
sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
int main() {
int t;
cin >> t;
for (int tt = 1; tt <= t; tt++) {
cout << "Case " << tt << ":" << endl;
me(sum, 0);
int n;
string s;
scanf("%d", &n);
build(1, n, 1);
while (cin >> s) {
if (s == "End")
break;
int a, b;
scanf("%d%d", &a, &b);
if (s == "Query")
printf("%d\n", num(a, b, 1, n, 1));
else if (s == "Add")
update(a, b, 1, n, 1);
else
update(a, -b, 1, n, 1);
}
}
return 0;
}
HDU 1698 Just a Hook(线段树)
题解:这个题看题就知道是一个很裸线段树的区间修改问题。
#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<stack>
#include<string>
const int maxn = 1e5 + 10;
const int mod = 1e9 + 7;
const int inf = 1e8;
#define me(a, b) memset(a,b,sizeof(a))
#define lowbit(x) x&(-x)
typedef long long ll;
using namespace std;
int num[maxn << 2], add[maxn << 2];
void up_data(int rt) {
num[rt] = num[rt << 1] + num[rt << 1 | 1];
}
void build(int l, int r, int rt) {
if (l == r) {
num[rt] = 1;
return;
}
int m = (l + r) >> 1;
build(l, m, rt << 1);
build(m + 1, r, rt << 1 | 1);
up_data(rt);
}
void push_down(int rt, int l, int r) {
if (add[rt]) {
int m = (l + r) >> 1;
num[rt << 1] = (m - l + 1) * add[rt];
num[rt << 1 | 1] = (r - m) * add[rt];
add[rt << 1] = add[rt << 1 | 1] = add[rt];
add[rt] = 0;
}
}
void push_data(int x, int L, int R, int l, int r, int rt) {
if (L <= l && R >= r) {
num[rt] = (r - l + 1) * x;
add[rt] = x;
return;
}
push_down(rt, l, r);
int m = (l + r) >> 1;
if (L <= m)
push_data(x, L, R, l, m, rt << 1);
if (R > m)
push_data(x, L, R, m + 1, r, rt << 1 | 1);
up_data(rt);
}
int main() {
int t, Case = 1;
cin >> t;
while (t--) {
me(num, 0), me(add, 0);
int n, m;
cin >> n;
build(1, n, 1);
cin >> m;
while (m--) {
int l, r, x;
scanf("%d%d%d", &l, &r, &x);
push_data(x, l, r, 1, n, 1);
}
cout << "Case " << Case++ << ": The total value of the hook is " << num[1] << "." << endl;
}
return 0;
}
POJ 3468 A Simple Problem with Integers(线段树区间修改+求和)
#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<stack>
#include<string>
const int maxn = 1e5 + 10;
const int mod = 1e9 + 7;
const int inf = 1e8;
#define me(a, b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
ll num[maxn << 2], add[maxn << 2];
void pushup(int rt) {
num[rt] = num[rt << 1] + num[rt << 1 | 1];
}
void build(int l, int r, int rt) {
if (l == r) {
scanf("%lld", &num[rt]);
return;
}
int m = (l + r) >> 1;
build(l, m, rt << 1);
build(m + 1, r, rt << 1 | 1);
pushup(rt);
}
void updata(int l, int r, int rt) {
if (add[rt]) {
int m = (l + r) >> 1;
add[rt << 1] += add[rt];
add[rt << 1 | 1] += add[rt];
num[rt << 1] += add[rt] * (m - l + 1);
num[rt << 1 | 1] += add[rt] * (r - m);
add[rt] = 0;
}
}
void pushdata(int L, int R, int c, int l, int r, int rt) {
if (L <= l && R >= r) {
num[rt] += (r - l + 1) * c, add[rt] += c;
return;
}
int m = (l + r) >> 1;
updata(l, r, rt);
if (L <= m)
pushdata(L, R, c, l, m, rt << 1);
if (R > m)
pushdata(L, R, c, m + 1, r, rt << 1 | 1);
pushup(rt);
}
ll getsum(int L, int R, int l, int r, int rt) {
if (L <= l && R >= r)
return num[rt];
ll sum = 0;
updata(l, r, rt);
int m = (l + r) >> 1;
if (L <= m)
sum += getsum(L, R, l, m, rt << 1);
if (R > m)
sum += getsum(L, R, m + 1, r, rt << 1 | 1);
return sum;
}
int main() {
int m, n;
while (cin >> n >> m) {
me(num, 0), me(add, 0);
build(1, n, 1);
while (m--) {
char s[5];
scanf("%s", s);
if (s[0] == 'Q') {
int x, y;
scanf("%d%d", &x, &y);
cout << getsum(x, y, 1, n, 1) << endl;
} else {
int x, y, c;
scanf("%d%d%d", &x, &y, &c);
pushdata(x, y, c, 1, n, 1);
}
}
}
return 0;
}
HDU 1540 Tunnel Warfare(最长连续区间+单点修改)
题意:
操作一:某个村庄被毁灭。
操作二:给出一个村庄的坐标,求包含包含这个村庄的的最长未被村庄的长度。
操作三:最后一个被摧毁的村庄被修复。
题解:首先我们可以想到用线段树来维护最长连续区间长度,我们现在可以用1代表改点没有被破坏,用0表示改点被破坏,然后用一个栈或数组来装上次破坏的点。然后就是线段树维护1的最长长度了。
#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<stack>
#include<string>
const int maxn = 5e4 + 5;
const int mod = 10007;
const int inf = 1e9;
const long long onf = 1e18;
#define me(a, b) memset(a,b,sizeof(a))
#define lowbit(x) x&(-x)
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#define PI 3.14159265358979323846
typedef long long ll;
typedef unsigned long long ull;
using namespace std;
int ls[maxn << 2], rs[maxn << 2], sum[maxn << 2];
///sum表示区间最长连续序列,ls表示从左端点开始最长连续前缀,rs表示从r开始最长后缀
int n, m;
void push_up(int l, int r, int rt) {
int mid = (l + r) >> 1;
ls[rt] = ls[rt << 1];
if (ls[rt << 1] == mid - l + 1)///要是左区间最长长度等于左区间长度,那么区间最长前缀长度要加上右儿子区间的最长前缀
ls[rt] += ls[rt << 1 | 1];
rs[rt] = rs[rt << 1 | 1];
if (rs[rt << 1 | 1] == r - mid)///与上面情况一样
rs[rt] += rs[rt << 1];
sum[rt] = max(ls[rt], rs[rt]);
sum[rt] = max(sum[rt], max(sum[rt << 1], sum[rt << 1 | 1]));
sum[rt] = max(sum[rt], rs[rt << 1] + ls[rt << 1 | 1]);
///区间的最长连续长度为,左区间,右区间最长连续长度,和左区间的最长后缀+右区间的最长前缀的三者最大。(最后一种情况表示最长连续区间在中间)
}
void build(int l, int r, int rt) {
if (l == r) {
ls[rt] = rs[rt] = sum[rt] = 1;///初始化所有点都没有被破坏
return;
}
int mid = (l + r) >> 1;
build(lson);
build(rson);
push_up(l, r, rt);
}
void push_date(int pos, int x, int l, int r, int rt) {
sum[rt] = ls[rt] = rs[rt] = 0;
if (l == r) {
sum[rt] = ls[rt] = rs[rt] = x;
return;
}
int mid = (l + r) >> 1;
if (pos <= mid)
push_date(pos, x, lson);
else
push_date(pos, x, rson);
push_up(l, r, rt);
}
int query(int pos, int l, int r, int rt) {
if (l == r)
return sum[rt];
int mid = (l + r) >> 1;
if (pos <= mid) {///点在左儿子区间里
if (pos >= mid - rs[rt << 1] + 1)
return ls[rt << 1 | 1] + rs[rt << 1];///要是点正好在左儿子的最长后缀中,肯定最长区间和就是左儿子的最长后缀长度+右儿子的最长前缀(左儿子的最长后缀与右儿子的最长前缀是连着的)
else
return query(pos, lson);///要是没有落在最长后缀中,就不用考虑是否加上右儿子的最长前缀,直接往下找就行了
} else {
if (pos <= mid + ls[rt << 1 | 1])///与上面情况类似
return ls[rt << 1 | 1] + rs[rt << 1];
else
return query(pos, rson);
}
}
int main() {
while (scanf("%d%d", &n, &m) != EOF) {
build(1, n, 1);
stack<int> q;
while (m--) {
char str[2];
int x;
scanf("%s", str);
if (str[0] == 'D') {
scanf("%d", &x);
push_date(x, 0, 1, n, 1);
q.push(x);
} else if (str[0] == 'Q') {
scanf("%d", &x);
printf("%d\n", query(x, 1, n, 1));
} else {
if (q.size()) {
push_date(q.top(), 1, 1, n, 1);
q.pop();
}
}
}
}
return 0;
}
洛谷 P3372 【模板】线段树 1
题解:裸的线段树。
#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<stack>
#include<string>
const int maxn = 1e5 + 10;
const int mod = 1e9 + 7;
const int inf = 1e8;
#define me(a, b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
ll sum[maxn << 2], add[maxn << 2];
void updata(int rt) {
sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
void lazy(int l, int r, int rt) {
if (add[rt]) {
int m = (l + r) >> 1;
sum[rt << 1] += (m - l + 1) * add[rt];
sum[rt << 1 | 1] += (r - m) * add[rt];
add[rt << 1] += add[rt];
add[rt << 1 | 1] += add[rt];
add[rt] = 0;
}
}
void build(int l, int r, int rt) {
if (l == r) {
scanf("%lld", &sum[rt]);
return;
}
int m = (l + r) >> 1;
build(l, m, rt << 1);
build(m + 1, r, rt << 1 | 1);
updata(rt);
}
void pushdata(int L, int R, int x, int l, int r, int rt) {
if (L <= l && R >= r) {
sum[rt] += (r - l + 1) * x;
add[rt] += x;
return;
}
lazy(l, r, rt);
int m = (l + r) >> 1;
if (L <= m)
pushdata(L, R, x, l, m, rt << 1);
if (R > m)
pushdata(L, R, x, m + 1, r, rt << 1 | 1);
updata(rt);
}
ll getsum(int L, int R, int l, int r, int rt) {
if (L <= l && R >= r)
return sum[rt];
lazy(l, r, rt);
int m = (l + r) >> 1;
ll s = 0;
if (L <= m)
s += getsum(L, R, l, m, rt << 1);
if (R > m)
s += getsum(L, R, m + 1, r, rt << 1 | 1);
return s;
}
int main() {
int n, m;
cin >> n >> m;
build(1, n, 1);
while (m--) {
int a, x, y, k;
scanf("%d", &a);
if (a == 1) {
scanf("%d%d%d", &x, &y, &k);
pushdata(x, y, k, 1, n, 1);
} else {
scanf("%d%d", &x, &y);
printf("%lld\n", getsum(x, y, 1, n, 1));
}
}
return 0;
}
洛谷 P3373 【模板】线段树 2
题解:还是一个线段树的裸题,但是在区间修改的时候,他要乘上一个数,这时候懒标记下放,是要把下面要加的懒标记和要乘的懒标记的都要下放。
#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<stack>
#include<string>
const int maxn = 1e5 + 10;
const int mod = 1e9 + 7;
const int inf = 1e8;
#define me(a, b) memset(a,b,sizeof(a))
#define lowbit(x) x&(-x)
typedef long long ll;
using namespace std;
ll num[maxn << 2], add[maxn << 2], ch[maxn << 2];
ll n, m, p;
void init(int n) {
for (int i = 1; i <= 4 * n; i++)
num[i] = add[i] = 0, ch[i] = 1;
}
void updata(int rt) {
num[rt] = (num[rt << 1] + num[rt << 1 | 1]) % p;
}
void build(int l, int r, int rt) {
if (l == r) {
scanf("%lld", &num[rt]);
return;
}
int m = (l + r) >> 1;
build(l, m, rt << 1);
build(m + 1, r, rt << 1 | 1);
updata(rt);
}
void push_down(int l, int r, int rt) {
int m = (l + r) >> 1;
add[rt << 1] = (add[rt << 1] * ch[rt] + add[rt]) % p;
add[rt << 1 | 1] = (add[rt << 1 | 1] * ch[rt] + add[rt]) % p;
ch[rt << 1] = (ch[rt << 1] * ch[rt]) % p;
ch[rt << 1 | 1] = (ch[rt << 1 | 1] * ch[rt]) % p;
num[rt << 1] = (num[rt << 1] * ch[rt] + (m - l + 1) * add[rt]) % p;
num[rt << 1 | 1] = (num[rt << 1 | 1] * ch[rt] + (r - m) * add[rt]) % p;
ch[rt] = 1, add[rt] = 0;
}
void push_data(int flog, int x, int L, int R, int l, int r, int rt) {
if (L <= l && R >= r) {
if (flog == 1) {
add[rt] = (add[rt] * x) % p;
ch[rt] = (ch[rt] * x) % p;
num[rt] = (num[rt] * x) % p;
} else {
add[rt] = (add[rt] + x) % p;
num[rt] = (num[rt] + (r - l + 1) * x) % p;
}
return;
}
int m = (l + r) >> 1;
push_down(l, r, rt);
if (L <= m)
push_data(flog, x, L, R, l, m, rt << 1);
if (R > m)
push_data(flog, x, L, R, m + 1, r, rt << 1 | 1);
updata(rt);
}
ll get_sum(int L, int R, int l, int r, int rt) {
if (L <= l && R >= r)
return num[rt];
int m = (l + r) >> 1;
push_down(l, r, rt);
ll s = 0;
if (L <= m)
s = (s + get_sum(L, R, l, m, rt << 1)) % p;
if (R > m)
s = (s + get_sum(L, R, m + 1, r, rt << 1 | 1)) % p;
return s % p;
}
int main() {
cin >> n >> m >> p;
init(n);
build(1, n, 1);
while (m--) {
int flog, x, y, k;
scanf("%d", &flog);
if (flog == 3) {
scanf("%d%d", &x, &y);
printf("%lld\n", get_sum(x, y, 1, n, 1));
} else {
scanf("%d%d%d", &x, &y, &k);
push_data(flog, k, x, y, 1, n, 1);
}
}
return 0;
}
洛谷 P1198 [JSOI2008]最大数(线段树或RMQ)
题解:这是一个裸的线段树和RMQ的题,这里把这两种代码都贴出来。
#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<stack>
#include<string>
const int maxn = 2e5 + 10;
const int mod = 1e9 + 7;
const int inf = 1e8;
#define me(a, b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
int ma[maxn][21], a[maxn];
void RMQ(int u) {
ma[u][0] = a[u];
for (int i = 1; (1 << i) <= u; i++)
ma[u][i] = max(ma[u][i - 1], ma[u - (1 << (i - 1))][i - 1]);
}
int getmax(int l, int r) {
int x = log(r - l + 1.0) / log(2.0);
return max(ma[l][x], ma[l + (1 << x) - 1][x]);
}
int main() {
int n, d;
cin >> n >> d;
int l = 0, t = 0;
while (n--) {
char str[5];
scanf("%s", str);
if (str[0] == 'A') {
int x;
scanf("%d", &x);
a[++l] = (x + t) % d;
RMQ(l);
} else {
int x;
scanf("%d", &x);
int ans = getmax(l - x + 1, l);
printf("%d\n", ans);
t = ans;
}
}
return 0;
}
线段树:
#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<stack>
#include<string>
const int maxn = 2e5 + 10;
const int mod = 1e9;
const int inf = 1e9;
#define me(a, b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
int ma[maxn << 2];
void updata(int rt) {
ma[rt] = max(ma[rt << 1], ma[rt << 1 | 1]);
}
void build(int l, int r, int rt) {
if (l == r) {
ma[rt] = -inf;
return;
}
int m = (l + r) >> 1;
build(l, m, rt << 1);
build(m + 1, r, rt << 1 | 1);
}
void charu(int c, int x, int l, int r, int rt) {
if (l == r) {
ma[rt] = x;
return;
}
int m = (l + r) >> 1;
if (c <= m)
charu(c, x, l, m, rt << 1);
else
charu(c, x, m + 1, r, rt << 1 | 1);
updata(rt);
}
int getmax(int L, int R, int l, int r, int rt) {
if (L <= l && R >= r)
return ma[rt];
int m = (l + r) >> 1;
int temp = -2147283647;
if (L <= m)
temp = max(temp, getmax(L, R, l, m, rt << 1));
if (R > m)
temp = max(temp, getmax(L, R, m + 1, r, rt << 1 | 1));
return temp;
}
int main() {
int n, d, temp = 0, l = 0;
cin >> n >> d;
build(1, maxn, 1);
while (n--) {
char str[5];
scanf("%s", str);
if (str[0] == 'A') {
int x;
scanf("%d", &x);
l++;
charu(l, (x + temp) % d, 1, maxn, 1);
} else {
int x;
scanf("%d", &x);
temp = getmax(l - x + 1, l, 1, maxn, 1);
printf("%d\n", temp);
}
}
return 0;
}
洛谷 P1531 I Hate It(线段树或树状数组)
题解:裸的线段树。
线段树:
#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<stack>
#include<string>
const int maxn = 2e5 + 10;
const int mod = 1e9 + 7;
const int inf = 1e8;
#define me(a, b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
int ma[maxn << 2];
void updata(int rt) {
ma[rt] = max(ma[rt << 1], ma[rt << 1 | 1]);
}
void build(int l, int r, int rt) {
if (l == r) {
scanf("%d", &ma[rt]);
return;
}
int m = (l + r) >> 1;
build(l, m, rt << 1);
build(m + 1, r, rt << 1 | 1);
updata(rt);
}
void pushdata(int c, int x, int l, int r, int rt) {
if (l == r) {
if (ma[rt] < x)
ma[rt] = x;
return;
}
int m = (l + r) >> 1;
if (c <= m)
pushdata(c, x, l, m, rt << 1);
else
pushdata(c, x, m + 1, r, rt << 1 | 1);
updata(rt);
}
int getmax(int L, int R, int l, int r, int rt) {
if (L <= l && R >= r)
return ma[rt];
int m = (l + r) >> 1;
int ma1 = 0, ma2 = 0;
if (L <= m)
ma1 = getmax(L, R, l, m, rt << 1);
if (R > m)
ma2 = getmax(L, R, m + 1, r, rt << 1 | 1);
return max(ma1, ma2);
}
int main() {
int n, m;
cin >> n >> m;
me(ma, 0);
build(1, n, 1);
while (m--) {
char str[2];
int a, b;
scanf("%s%d%d", str, &a, &b);
if (str[0] == 'Q')
printf("%d\n", getmax(a, b, 1, n, 1));
else
pushdata(a, b, 1, n, 1);
}
return 0;
}
2018年全国多校算法寒假训练营练习比赛(第五场)逆序数(树状数组)
题解:裸的数组树状数组求逆序数,所以可以声明一个数组,记录比当前位置大的数,因为逆序数,正好是前面出现的比后面大的数,所以只要在前面输入一个数,就把比他小的数的数组加一,最后直接加数组里的数,就是前面比当前输入数大的数的个数。
#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<string>
#include<map>
#include<cmath>
#include<vector>
const int maxn = 5e5 + 5;
const int mod = 1e9 + 7;
#define me(a, b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
int bit[maxn], n;
struct node {
int x, i;
bool friend operator<(node a, node b) {
if (a.x == b.x)
return a.i < b.i;
return a.x < b.x;
}
} a[maxn];
int lowbit(int x) {
return x & (-x);
}
void updata(int x) {
while (x <= n) {
bit[x] += 1;
x += lowbit(x);
}
}
int ss(int x) {
int s = 0;
while (x) {
s += bit[x];
x -= lowbit(x);
}
return s;
}
int main() {
while (~scanf("%d", &n) && n) {
me(bit, 0);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i].x);
a[i].i = i;
}
sort(a + 1, a + 1 + n);
ll sum = 0;
for (int i = 1; i <= n; i++) {
updata(a[i].i);
sum += i - ss(a[i].i);
}
cout << sum << endl;
}
return 0;
}
2018年全国多校算法寒假训练营练习比赛(第五场)H Tree Recovery(线段树)
#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<stack>
#include<string>
const int maxn = 1e5 + 5;
const int mod = 1e9 + 7;
const int inf = 1e9;
#define me(a, b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
int num[maxn << 2], add[maxn << 2];
void build(int l, int r, int rt) {
if (l == r) {
scanf("%d", &num[rt]);
return;
}
int m = (l + r) >> 1;
build(l, m, rt << 1);
build(m + 1, r, rt << 1 | 1);
num[rt] = num[rt << 1] + num[rt << 1 | 1];
}
void pushdown(int ln, int rn, int rt) {
if (add[rt]) {
add[rt << 1] += add[rt];
add[rt << 1 | 1] += add[rt];
num[rt << 1] += add[rt] * ln;
num[rt << 1 | 1] += add[rt] * rn;
add[rt] = 0;
}
}
void updata(int L, int R, int c, int l, int r, int rt) {
if (L <= l && R >= r) {
num[rt] += (r - l + 1) * c;
add[rt] += c;
return;
}
int m = (l + r) >> 1;
pushdown(m - l + 1, r - m, rt);
if (L <= m)
updata(L, R, c, l, m, rt << 1);
if (R > m)
updata(L, R, c, m + 1, r, rt << 1 | 1);
num[rt] = num[rt << 1] + num[rt << 1 | 1];
}
int query(int L, int R, int l, int r, int rt) {
if (L <= l && R >= r)
return num[rt];
int s = 0;
int m = (l + r) >> 1;
pushdown(m - l + 1, r - m, rt);
if (L <= m)
s += query(L, R, l, m, rt << 1);
if (R > m)
s += query(L, R, m + 1, r, rt << 1 | 1);
return s;
}
int main() {
int n, m;
cin >> n >> m;
build(1, n, 1);
while (m--) {
string s;
int a, b, c;
cin >> s;
if (s == "Q") {
scanf("%d%d", &a, &b);
cout << query(a, b, 1, n, 1) << endl;
} else {
scanf("%d%d%d", &a, &b, &c);
updata(a, b, c, 1, n, 1);
}
}
return 0;
}
power oj 2821: 小Y学长的GCD难题(线段树区间求最大公因数+区间修改)
#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<stack>
#include<string>
const int maxn = 2e5 + 10;
const int mod = 1e9 + 7;
const int inf = 1e8;
#define me(a, b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
int d[maxn << 2], lazy[maxn << 2];
int gcd(int a, int b) {
if (!b)
return a;
return gcd(b, a % b);
}
void update(int rt) {
d[rt] = gcd(d[rt << 1], d[rt << 1 | 1]);
}
void build(int l, int r, int rt) {
if (l == r) {
scanf("%d", &d[rt]);
return;
}
int m = (l + r) >> 1;
build(l, m, rt << 1);
build(m + 1, r, rt << 1 | 1);
update(rt);
}
void pushdown(int rt) {
if (lazy[rt]) {
d[rt << 1] = d[rt << 1 | 1] = lazy[rt];
lazy[rt << 1] = lazy[rt << 1 | 1] = lazy[rt];
lazy[rt] = 0;
}
}
void pushdate(int L, int R, int c, int l, int r, int rt) {
if (L <= l && R >= r) {
d[rt] = lazy[rt] = c;
return;
}
pushdown(rt);
int m = (l + r) >> 1;
if (L <= m)
pushdate(L, R, c, l, m, rt << 1);
if (R > m)
pushdate(L, R, c, m + 1, r, rt << 1 | 1);
update(rt);
}
int getgcd(int L, int R, int l, int r, int rt) {
if (L <= l && R >= r)
return d[rt];
pushdown(rt);
int m = (l + r) >> 1;
int a = 0, b = 0;
if (L <= m)
a = getgcd(L, R, l, m, rt << 1);
if (R > m)
b = getgcd(L, R, m + 1, r, rt << 1 | 1);
return gcd(a, b);
}
int main() {
int t;
cin >> t;
while (t--) {
int n, q;
cin >> n >> q;
me(d, 0), me(lazy, 0);
build(1, n, 1);
while (q--) {
int x, a, b, c;
scanf("%d", &x);
if (x == 1) {
scanf("%d%d%d", &a, &b, &c);
pushdate(min(a, b), max(a, b), c, 1, n, 1);
} else {
scanf("%d%d", &a, &b);
printf("%d\n", getgcd(min(a, b), max(a, b), 1, n, 1));
}
}
}
return 0;
}
区间 (interval)(牛客小白月赛5)(差分)
题解:可以用线段树做,但是那样有点麻烦,这里可以用差分,这样只用更新,边界节点了。具体思想可以看代码理解。
#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
const int maxn = 1e6 + 10;
const int mod = 1e9 + 7;
const int inf = 1e9;
#define me(a) memset(a,0,sizeof(a))
typedef long long ll;
using namespace std;
ll num[maxn], a[maxn];
int main() {
int n, m;
while (cin >> n >> m) {
me(num);
for (int i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
num[i] = a[i] - a[i - 1];
}
while (m--) {
int q, l, r, p;
scanf("%d%d%d%d", &q, &l, &r, &p);
if (q == 1) {
num[l] -= p;
num[r + 1] += p;
} else {
num[l] += p;
num[r + 1] -= p;
}
}
for (int i = 1; i <= n; i++)
a[i] = a[i - 1] + num[i];
int x, y;
cin >> x >> y;
ll sum = 0;
for (int i = x; i <= y; i++)
sum += a[i];
cout << sum << endl;
}
return 0;
}
POJ2299 Ultra-QuickSort(树状数组求逆序数)
题解:前面已经有类似题了,但是没有说清楚,这里说的比较详细点。
这个需要用到离散化,
建立一个结构体包含val和id, val就是输入的数,id表示输入的顺序。然后按照val从小到大排序,如果val相等,那么就按照id排序。如果没有逆序的话,肯定id是跟i(表示拍好后的顺序)一直一样的,如果有逆序数,那么有的i和id是不一样的。所以,利用树状数组的特性,我们可以简单的算出逆序数的个数。
如果还是不明白的话举个例子。(输入4个数)
输入:9 -1 18 5
输出 3.
输入之后对应的结构体就会变成这样
val:9 -1 18 5
id: 1 2 3 4
排好序之后就变成了
val : -1 5 9 18
id: 2 4 1 3
2 4 1 3 的逆序数 也是3
之后再利用树状数组的特性就可以解决问题了;
因为数字可能有重复, 所以添加操作不再单纯的置为1 ,而是 ++;
#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<string>
#include<map>
#include<cmath>
#include<vector>
const int maxn = 5e5 + 5;
const int mod = 1e9 + 7;
#define me(a, b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
int bit[maxn], n;
struct node {
int x, i;
bool friend operator<(node a, node b) {
if (a.x == b.x)
return a.i < b.i;
return a.x < b.x;
}
} a[maxn];
int lowbit(int x) {
return x & (-x);
}
void updata(int x) {
while (x <= n) {
bit[x] += 1;
x += lowbit(x);
}
}
int ss(int x) {
int s = 0;
while (x) {
s += bit[x];
x -= lowbit(x);
}
return s;
}
int main() {
while (~scanf("%d", &n) && n) {
me(bit, 0);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i].x);
a[i].i = i;
}
sort(a + 1, a + 1 + n);
ll sum = 0;
for (int i = 1; i <= n; i++) {
updata(a[i].i);
sum += i - ss(a[i].i);
}
cout << sum << endl;
}
return 0;
}
HDU4000 Fruit Ninja(树状数组)
题意:给出n个数,求所有的i<j<k且a[i]<a[k]<a[j]的个数。
题解:题目也就是让最后那个数第二大。我们输入第i个数字a,那么我们用的树状数组存的就是sum(a-1),sum(a-1)表示在a这个位置,在前i-1个数中,有sum(a-1)个数比a小,那么我们就可以求出在后面的(i+1,n)的序列中,有R=(n-a)-(i-1-sum(a-1))个数比a要大,(n-a)表示有这么多个数比a大,(i-1-sum(a-1))表示前i-1个数里面有sum(a-1)个数比a大,那么R=(n-a)-(i-1-sum(a-1))就表示后面有多少个数比a要大。然后,我们思考一下,a当作三个数里面的最小值,然后让它们组合,是不是就是有C(R,2)即R*(R-1)/2,因为后面的序列都是固定的,所以并没有排序的概念,也就是说,原本序列1 5 4 ,那么我们没有必要去思考1 4 5的情况,因为它已经固定好了,所以后面的序列选的时候直接从R个选2个就可以了,然后C(R,2)表示所有的a+两个大的(也就是小中大,小大中都包含在里面了),那么我们要求的是小大中,所以对于每一个a来说,我们都需要减去小中大的情况,那么小中大=sum(a-1)*R,所以最后答案就是对于(1,n)所有数,求和SUM(C(R,2)-sum(a-1)*R。
#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<string>
#include<map>
#include<cmath>
#include<vector>
const int maxn = 5e5 + 5;
const int mod = 1e8 + 7;
#define me(a, b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
int n, bit[maxn];
int lowbit(int x) {
return x & (-x);
}
void updata(int x) {
while (x <= n) {
bit[x]++;
x += lowbit(x);
}
}
int getsum(int x) {
int s = 0;
while (x) {
s += bit[x];
x -= lowbit(x);
}
return s;
}
int main() {
int t, Case = 0;
cin >> t;
while (t--) {
me(bit, 0);
scanf("%d", &n);
ll sum = 0, x, l, r;///l,前面输入比x小的数的个数,r表示输入数中比x大的数的个数。
for (int i = 0; i < n; i++) {
scanf("%lld", &x);
updata(x);
l = getsum(x - 1);
r = (n - x) - (i - l - 1);///(n-x)表示有这么多个数比x大,(i-1-l)表示前i-1个数里面有sum(x-1)个数比a大
sum += r * (r - 1) / 2 - r * l;
}
printf("Case #%d: %lld\n", ++Case, sum % mod);
}
return 0;
}
POJ2481 Cows(树状数组)
题意:先说下题意吧,其实就是输入一些线段,问能找到这条线段最多包含几条线段If Si <= Sj and Ej <= Ei and Ei - Si > Ej - Sj。
题解:这样问题就很简单了,我们可以按照后节点从大到小排序,如果后节点相等,前节点从小到大排序。这样排完序后,我们只需要找前面比前节点小的节点的个数就行了。
#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<string>
#include<map>
#include<cmath>
#include<set>
#include<vector>
const int maxn = 1e5 + 10;
const int mod = 1e9 + 7;
#define me(a, b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
int n, bit[maxn];
struct node {
int s, e, i;
bool friend operator<(node a, node b) {
if (a.e == b.e)
return a.s < b.s;
return a.e > b.e;
}
} a[maxn];
int lowbit(int x) {
return x & (-x);
}
void updata(int x) {
while (x <= n) {
bit[x]++;
x += lowbit(x);
}
}
int getsum(int x) {
int sum = 0;
while (x) {
sum += bit[x];
x -= lowbit(x);
}
return sum;
}
int main() {
while (cin >> n && n) {
int s[maxn];
me(bit, 0);
for (int i = 1; i <= n; i++) {
scanf("%d%d", &a[i].s, &a[i].e);
a[i].i = i;
a[i].s++;///因为s可能会为0,所以这里都加一
}
sort(a + 1, a + n + 1);
for (int i = 1; i <= n; i++) {
if (a[i].s == a[i - 1].s && a[i].e == a[i - 1].e)///若两区间相等,该值等于上一个的值
s[a[i].i] = s[a[i - 1].i];
else
s[a[i].i] = getsum(a[i].s);///更新节点信息
updata(a[i].s);
}
for (int i = 1; i <= n; i++) {
if (i != n)
printf("%d ", s[i]);
else
printf("%d\n", s[i]);
}
}
return 0;
}
POJ 1990 MooFest(树状数组)
题意:当两头牛i,j交流的时候,交流的最小声音为max{v[i],v[j]}*他们之间的距离。现在有n头牛,求他们之间两两交流最少要的音量和。
题解:如果用暴力的话肯定会超时的,所以现在可以用树状数组。首先将牛按发出声音的值,按从小到大排序。然后声明两个数组,一个记录当前输入的牛的前面位置比它小的牛的个数,一个记录这些牛的位置和。现在就是怎么求当前牛与其他牛的不同值了。这个分两部分计算,一个是位置比当前牛小的牛和位置比当前牛大的牛。位置小的牛很好计算,就是当前牛的听力值乘以比当前牛位置小的所有牛的位置差。比它大的牛计算有点麻烦。现在用树状数组求出当前输入的所有牛的位置和,减去位置比它小的牛的位置和,位置比它大的牛的位置和。在乘以他的听力值,就是这头牛与其他牛的所有不同值得和,因为按听力排序的,所以当前牛的听力值是最大的,直接乘就行了。
#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<stack>
#include<string>
const int maxn = 2e4 + 5;
const int mod = 1e9 + 7;
const int inf = 1e9;
#define me(a, b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
ll s[maxn], c[maxn];
struct node {
int v, i;
bool friend operator<(node a, node b) {
if (a.v == b.v)
return a.i < b.i;
return a.v < b.v;
}
} a[maxn];
int lowbit(int x) {
return x & (-x);
}
void update(ll a[], int x, int s) {
while (x <= maxn) {
a[x] += s;
x += lowbit(x);
}
}
ll getsum(ll a[], int x) {
int sum = 0;
while (x) {
sum += a[x];
x -= lowbit(x);
}
return sum;
}
int main() {
int n;
while (~scanf("%d", &n)) {
for (int i = 0; i < n; i++)
scanf("%lld%lld", &a[i].v, &a[i].i);
sort(a, a + n);
ll sum = 0;
for (int i = 0; i < n; i++) {
ll xzs = getsum(c, a[i].i);//下标比他小的牛的个数
ll xs = getsum(s, a[i].i);//下标比他小的牛的下标和
ll ds = getsum(s, maxn);//当前输入所有牛的下标和
sum += a[i].v * (xzs * a[i].i - xs + (ds - xs) - (i - xzs) * a[i].i);
//(xzs*a[i].i-xs),位置比当前牛小的位置差的和。((ds-xs)-(i-xzs)*a[i].i)比当前牛位置大的位置差的和。
update(c, a[i].i, 1);
update(s, a[i].i, a[i].i);
}
printf("%lld\n", sum);
}
return 0;
}
POJ 3264 Balanced Lineup(RMQ)
题解:这个题可以用线段树维护,但是这里用RMQ更方便。
#include <iostream>
#include<cstring>
#include<cstdio>
#include<algorithm>
#include<map>
#include<queue>
#include<set>
#include<cmath>
#include<stack>
#include<string>
const int maxn = 5e4 + 10;
const int mod = 1e9 + 7;
const int inf = 1e8;
#define me(a, b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
int ma[maxn][40], mi[maxn][40], n, m;
void inct() {
for (int j = 1; (1 << j) <= n; j++)
for (int i = 1; i + (1 << j) - 1 <= n; i++) {
ma[i][j] = max(ma[i][j - 1], ma[i + (1 << (j - 1))][j - 1]);
mi[i][j] = min(mi[i][j - 1], mi[i + (1 << (j - 1))][j - 1]);
}
}
void print(int l, int r) {
int k = (int) (log(r - l + 1 + 0.0) / log(2.0));///k = log2(r - l + 1);
int y = max(ma[l][k], ma[r - (1 << k) + 1][k]);
int x = min(mi[l][k], mi[r - (1 << k) + 1][k]);
cout << y - x << endl;
}
int main() {
while (~scanf("%d%d", &n, &m)) {
me(ma, 0), me(mi, 0);
for (int i = 1; i <= n; i++) {
scanf("%d", &ma[i][0]);
mi[i][0] = ma[i][0];
}
inct();
while (m--) {
int l, r;
cin >> l >> r;
print(l, r);
}
}
return 0;
}