HDU 1166 敌兵布阵
思路
线段树单点修改模板
代码
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cstdio>
using namespace std;
const int N = 50010;
struct Node {
int l, r;
int sum;
} tr[N * 4];
int w[N];
void pushup(int u) {
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
void build(int u, int l, int r) {
if(l == r) {
tr[u] = {l, l, w[l]};
}
else {
tr[u] = {l, r};
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
}
void modify(int u, int x, int v) {
if (tr[u].l == x && tr[u].r == x) {
tr[u].sum += v;
}
else {
int mid = tr[u].l + tr[u].r >> 1;
if(x <= mid)
modify(u << 1, x, v);
else
modify(u << 1 | 1, x, v);
pushup(u);
}
}
int query(int u, int l, int r) {
if(tr[u].l >= l && tr[u].r <= r)
return tr[u].sum;
else {
int sum = 0;
int mid = tr[u].l + tr[u].r >> 1;
if(l <= mid)
sum += query(u << 1, l, r);
if(r > mid)
sum += query(u << 1 | 1, l, r);
return sum;
}
}
int main() {
int T;
scanf("%d", &T);
int id = 0;
while(T --) {
printf("Case %d:\n", ++id);
int n;
scanf("%d", &n);
for (int i = 1; i <= n; i ++)
scanf("%d", &w[i]);
build(1, 1, n);
string op;
int l, r;
while(cin >> op , op != "End") {
scanf("%d%d", &l, &r);
if(op[0] == 'A')
modify(1, l, r);
else if(op[0] == 'S')
modify(1, l, -r);
else
printf("%d\n", query(1, l, r));
}
}
return 0;
}
HDU 1754 I hate it
思路
线段树单点修改模板
代码
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 200010;
struct Node {
int l, r;
int v;
} tr[N * 4];
int w[N];
void pushup(int u) {
tr[u].v = max(tr[u << 1].v, tr[u << 1 | 1].v);
}
void build(int u, int l, int r) {
if(l == r) {
tr[u] = {l, r, w[l]};
}
else {
tr[u] = {l, r};
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
}
void modify(int u, int x, int val) {
if(tr[u].l == x && tr[u].r == x) {
tr[u].v = val;
}
else {
int mid = tr[u].l + tr[u].r >> 1;
if(x <= mid)
modify(u << 1, x, val);
else
modify(u << 1 | 1, x, val);
pushup(u);
}
}
int query(int u, int l, int r) {
if(tr[u].l >= l && tr[u].r <= r)
return tr[u].v;
else {
int mid = tr[u].l + tr[u].r >> 1;
int maxx = 0;
if(l <= mid)
maxx = query(u << 1, l, r);
if(r > mid)
maxx = max(maxx, query(u << 1 | 1, l, r));
return maxx;
}
}
int main() {
int n, m;
while(~scanf("%d%d", &n, &m)) {
for (int i = 1; i <= n; i ++)
scanf("%d", &w[i]);
build(1, 1, n);
while(m --) {
char op[2];
int l, r;
scanf("%s%d%d", op, &l, &r);
if(op[0] == 'U') {
modify(1, l, r);
}
else {
printf("%d\n", query(1, l, r));
}
}
}
return 0;
}
POJ 3468 A Simple Problem with Integers
思路
线段树区间修改区间查询模板
代码
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cstdio>
using namespace std;
const int N = 100010;
typedef long long LL;
struct Node {
int l, r;
LL sum, add;
} tr[N * 4];
int w[N];
void pushup(int u) {
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
void pushdown(int u){
if(tr[u].add) {
tr[u << 1].add += tr[u].add, tr[u << 1].sum += (LL)(tr[u << 1].r - tr[u << 1].l + 1) * tr[u].add;
tr[u << 1 | 1].add += tr[u].add, tr[u << 1 | 1].sum += (LL)(tr[u << 1 | 1].r - tr[u << 1 | 1].l + 1) * tr[u].add;
tr[u].add = 0;
}
}
void build(int u, int l, int r) {
if(l == r)
tr[u] = {l, l, w[l], 0};
else {
tr[u] = {l, r};
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
}
void modify(int u, int l, int r, int d) {
if(tr[u].l >= l && tr[u].r <= r) {
tr[u].sum += (LL)(tr[u].r - tr[u].l + 1) * d;
tr[u].add += d;
}
else {
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if(l <= mid)
modify(u << 1, l, r, d);
if(r > mid)
modify(u << 1 | 1, l, r, d);
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()
{
int n, m;
scanf("%d%d", &n, &m);
for (int i = 1; i <= n; i ++) {
scanf("%d", &w[i]);
}
build(1, 1, n);
while(m --) {
char op[2];
int l, r;
scanf("%s%d%d", op, &l, &r);
if(op[0] == 'Q') {
printf("%lld\n", query(1, l, r));
}
else {
int c;
scanf("%d", &c);
modify(1, l, r, c);
}
}
return 0;
}
POJ - 2528 Mayor’s posters
思路
借鉴作者: verden
对区间操作,即对区间进行染色,线段树
不过此题数据范围较大,需要用到保序离散化。
注意离散化的时候,当两个点的距离大于1,需要新增一个点。
原因:[1,10],[1,4],[4,6]
离散化为:[1,4],[1,2] ,[3, 4]
区间查询的时候只会查询到两种颜色
- pushdown的时候,如果当前区间是同一种颜色(懒标记是1),这个区间存储的color有意义,需要传给left和right,把left和right的懒标记置为1(表示当前整个区间是同一种颜色)
- 在query时,如果当前区间的懒标记是1就可以把color插入结果,直接返回,否则一直查询到子节点
代码
#include<iostream>
#include<cstring>
#include<algorithm>
#include<set>
#include<vector>
#define rep(i, a, b) for (int i = a; i <= b; i ++)
#define x first
#define y second
using namespace std;
typedef pair<int, int> PII;
const int N = 1e4 + 100;
int n;
vector<int> v;
set<int> s;
PII a[N];
struct Node {
// color,该区间的颜色
int l, r, col;
// 对标记的理解:父亲修改,儿子暂时尚未修改, 减少所有节点都更新的开销
bool flag; // 懒标记:当前区间是否是同一种颜色
} tr[N << 4];
// 多组数据初始化
void init() {
s.clear();
v.clear();
}
void build(int u, int l, int r) {
tr[u] = {l, r, 0, 1};
if(l != r) {
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
}
}
// 对pushdown的理解:当要对儿子操作或者查询,需要进行修改,并把标记传下去
void pushdown(int u) {
Node &root = tr[u], &left = tr[u << 1], &right = tr[u << 1 | 1];
if(root.flag) {
left.flag = right.flag = 1;
left.col = right.col = root.col;
root.flag = 0;
}
}
void modify(int u, int l, int r, int color) {
if(tr[u].l >= l && tr[u].r <= r) {
tr[u].col = color;
tr[u].flag = 1;
}
else {
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if(l <= mid)
modify(u << 1, l, r, color);
if(r > mid)
modify(u << 1 | 1, l, r, color);
}
}
void query(int u, int l, int r) {
if(tr[u].l == tr[u].r || tr[u].flag) {
if(tr[u].col) {
s.insert(tr[u].col);
}
}
else {
int mid = tr[u].l + tr[u].r >> 1;
if(l <= mid)
query(u << 1, l, r);
if(r > mid)
query(u << 1 | 1, l, r);
}
}
int main() {
int T;
cin >> T;
while(T --) {
cin >> n;
init();
rep(i, 1, n) {
cin >> a[i].x >> a[i].y;
v.push_back(a[i].x);
v.push_back(a[i].y);
}
sort(v.begin(), v.end());
int Size = v.size();
// 加点
rep(i, 1, Size - 1)
if(v[i] - v[i - 1] > 1) {
v.push_back(v[i - 1] + 1);
}
// 离散化
sort(v.begin(), v.end());
v.erase(unique(v.begin(), v.end()), v.end());
build(1, 1, v.size());
rep(i, 1, n) {
a[i].x = lower_bound(v.begin(), v.end(), a[i].x) - v.begin() + 1; // 偏移1个位置
a[i].y = lower_bound(v.begin(), v.end(), a[i].y) - v.begin() + 1; // 偏移1个位置
modify(1, a[i].x, a[i].y, i);
}
query(1, 1, v.size());
cout << s.size() << endl;
}
}
HDU - 1698 Just a Hook
思路
区间修改的模板题。
(为什么每次调试的时候我都忘了把N该回来了!!!牢记)
代码
#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
const int N = 100010;
typedef long long LL;
struct Node {
int l, r;
int sum;
int lazy;
} tr[N << 2];
void pushdown(int u) {
Node &root = tr[u], &left = tr[u << 1], &right = tr[u << 1 | 1];
if(root.lazy) {
left.lazy = root.lazy, left.sum = (LL)(left.r - left.l + 1) * root.lazy;
right.lazy = root.lazy, right.sum = (LL)(right.r - right.l + 1) * root.lazy;
root.lazy = 0;
}
}
void pushup(int u) {
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
void build(int u, int l, int r) {
if(l == r)
tr[u] = {l, l, 1, 0};
else {
tr[u] = {l, r};
tr[u].lazy = 0;
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
}
void modify(int u, int l, int r, int x) {
if(tr[u].l >= l && tr[u].r <= r) {
tr[u].sum = (LL)(tr[u].r - tr[u].l + 1) * x;
tr[u].lazy = x;
}
else {
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
if(l <= mid)
modify(u << 1, l, r, x);
if(r > mid)
modify(u << 1 | 1, l, r, x);
pushup(u);
}
}
int query(int u, int l, int r){
if(tr[u].l >= l && tr[u].r <= r) {
return tr[u].sum;
}
else {
pushdown(u);
int mid = tr[u].l + tr[u].r >> 1;
int 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(){
int T;
scanf("%d", &T);
int id = 0;
while(T --) {
int n;
scanf("%d", &n);
build(1, 1, n);
int q;
scanf("%d", &q);
while(q --) {
int l, r, val;
scanf("%d%d%d", &l, &r, &val);
modify(1, l, r, val);
}
printf("Case %d: The total value of the hook is %d.\n", ++id, query(1, 1, n));
}
return 0;
}
POJ - 3264 Balanced Lineup
题意:
多次询问区间的最大值和最小值的差
思路
这题用ST表直接过,也是板子
代码
#include<iostream>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<cstdio>
using namespace std;
const int N = 50010, M = 16;
int f1[N][M], f2[N][M];
int w[N];
int n, m;
void init() {
for (int j = 0; j < M; j ++)
for (int i = 1; i + (1 << j) - 1 <= n; i ++) {
if(!j)
f1[i][j] = w[i];
else {
f1[i][j] = max(f1[i][j - 1], f1[i + (1 << j - 1)][j - 1]);
}
}
for (int j = 0; j < M; j ++)
for (int i = 1; i + (1 << j) - 1 <= n; i ++) {
if(!j)
f2[i][j] = w[i];
else {
f2[i][j] = min(f2[i][j - 1], f2[i + (1 << j - 1)][j - 1]);
}
}
}
int query(int l, int r) {
int len = r - l + 1;
int k = log(len) / log(2);
int maxx = max(f1[l][k], f1[r - (1 << k) + 1][k]);
int minx = min(f2[l][k], f2[r - (1 << k) + 1][k]);
return maxx - minx;
}
int main()
{
while(~scanf("%d%d", &n, &m)) {
for (int i = 1; i <= n; i ++)
scanf("%d", &w[i]);
init();
while(m --) {
int l, r;
scanf("%d%d", &l, &r);
int ans = query(l, r);
printf("%d\n", ans);
}
}
return 0;
}
HDU - 4027 Can you answer these queries?
题意
区间修改:将区间内所有数开根号,向下取整
区间查询:求区间内和
思路
因为一个数最大是2^63,最多会开根号开6次,该值就变为1了,所以对区间开根号的时候,可以将区间的所有点依次开根号,即对单点操作,不会超时
代码
#include<iostream>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
const int N = 100010;
typedef long long LL;
struct Node {
int l, r;
LL sum;
} tr[N * 4];
LL w[N];
void pushdown(int u) {
tr[u].sum = sqrt(tr[u].sum);
}
void pushup(int u) {
tr[u].sum = tr[u << 1].sum + tr[u << 1 | 1].sum;
}
void build(int u, int l, int r) {
if(l == r) {
tr[u] = {l, l, w[l]};
}
else {
tr[u] = {l, r};
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
}
void modify(int u, int l, int r) {
if(tr[u].r - tr[u].l + 1 == tr[u].sum)
return;
if(tr[u].l == tr[u].r) {
pushdown(u);
return;
}
int mid = tr[u].l + tr[u].r >> 1;
if(l <= mid)
modify(u << 1, l, r);
if(r > mid)
modify(u << 1 | 1, l, r);
pushup(u);
}
LL query(int u, int l, int r) {
if(tr[u].l >= l && tr[u].r <= r)
return tr[u].sum;
else {
LL 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() {
LL n;
int id = 0;
while(~scanf("%lld", &n)) {
printf("Case #%d:\n", ++id);
for (int i = 1; i <= n; i ++)
scanf("%lld", &w[i]);
build(1, 1, n);
LL m;
scanf("%lld", &m);
while(m --) {
LL op, l, r;
scanf("%lld%lld%lld", &op, &l, &r);
if(l > r)
swap(l, r);
if(!op)
modify(1, l, r);
else {
printf("%lld\n", query(1, l, r));
}
}
printf("\n");
}
return 0;
}
HDU 1540 Tunnel Warfare
题意
一个长度为n的序列初始值都为1
给定3个操作
- 单点修改,把某点改为0
- 单点修改,把最近被修改为0的点 修改成1
- 区间查询,查询该点所在位置能构成最长的连续1的个数
思路
维护改区间最大连续1前缀lmax
,最大连续1后缀rmax
,最大连续ltmax
如何查询?
该点在一个特定区间root
区间内,查询该点在该区间的最长连续1的个数。
先判断 该点具体在左半区left
,还是在右半区 right
。
我们先只考虑假如该点具体在左半区left
区间的情况。
如果left
区间的rmax
比从x位置
到left
区间的末尾还要大,说明从x
位置开始后半段加上x
的前小段都是1,并且甚至可以跨区间到right
区间的前缀1.
所以return tr[u << 1].rmax + tr[u << 1 | 1].lmax
即可
否则的话,就继续对左区间递归查询。
若该点在右半区区间的话同理。
代码
#include<cstring>
#include<algorithm>
#include<iostream>
using namespace std;
const int N = 50010;
struct Node {
int l, r;
// 这sum可不加,因为权值是0,1,可直接算
// 最大连续1前缀,最大后缀,最大连续
int lmax, rmax, tmax;
} tr[N << 2];
int n, m;
int top, stk[N];
void pushup(int u) {
Node &root = tr[u], &left = tr[u << 1], &right = tr[u << 1 | 1];
int mid = root.l + root.r >> 1;
root.lmax = left.lmax + (left.lmax == mid - root.l + 1 ? right.lmax : 0);
root.rmax = right.rmax + (right.rmax == root.r - mid ? left.rmax : 0);
root.tmax = max(max(left.tmax, right.tmax), left.rmax + right.lmax);
}
void build(int u, int l, int r) {
if(l == r) {
tr[u] = {l, r, 1, 1, 1};
}
else {
tr[u] = {l, r};
int mid = l + r >> 1;
build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
pushup(u);
}
}
void modify(int u, int x, int v) {
if (tr[u].l == tr[u].r) {
tr[u].tmax = tr[u].lmax = tr[u].rmax = v;
}
else {
int mid = tr[u].l + tr[u].r >> 1;
if(x <= mid)
modify(u << 1, x, v);
else
modify(u << 1 | 1, x, v);
pushup(u);
}
}
int query(int u, int x) {
if(tr[u].tmax == 0 || tr[u].l == tr[u].r){
return tr[u].tmax;
}
else {
int mid = tr[u].l + tr[u].r >> 1;
if(x <= mid) {
if(mid - x + 1 <= tr[u << 1].rmax) {
return tr[u << 1].rmax + tr[u << 1 | 1].lmax;
}
else
return query(u << 1, x);
}
else {
if(x - mid <= tr[u << 1 | 1].lmax) {
return tr[u << 1].rmax + tr[u << 1 | 1].lmax;
}
else
return query(u << 1 | 1, x);
}
}
}
int main() {
while(~scanf("%d%d", &n, &m)) {
build(1, 1, n);
top = 0;
for (int i = 1; i <= m; i ++) {
char op[2];
int x;
scanf("%s", op);
if(op[0] == 'D') {
scanf("%d", &x);
stk[++ top] = x;
modify(1, x, 0);
}
else if(op[0] == 'Q') {
scanf("%d", &x);
printf("%d\n", query(1, x));
}
else
modify(1, stk[top--], 1);
}
}
return 0;
}