[kuangbin]
专题7 线段树 题解 + 总结
kuangbin带你飞
:点击进入新世界
kuangbin
专题十二 基础DP1 题解+总结:https://www.cnblogs.com/RioTian/p/13110438.html
kuangbin
专题六 最小生成树 题解+总结:https://www.cnblogs.com/RioTian/p/13380764.html
[kuangbin]
专题九 连通图 题解+总结 : https://www.cnblogs.com/RioTian/p/13395039.html
线段树相关知识:树状数组 、 线段树 、离散化
因为专题是后面制作,所以可能部分题目题解会需要转去我的另一篇题解记录上
总结:
待补。。。
1. 敌兵布阵
HDU - 1166
思路:
基础模板题
三种思路:维护一个前缀数组,但容易超时、树状数组、线段树
题解链接
2. I Hate It
HDU - 1754
题意理解很简单,这里就说下注意点,数组要开大点不然容易RE,同时因为数据较大,可以采用快读或者scanf
#include
using namespace std;
#define ms(x, n) memset(x,n,sizeof(x));
typedef long long LL;
const int inf = 1<<30;
const LL maxn = 200000+5;
int N, M, a[maxn];
int maxA[maxn*4];
void pushup(int id){maxA[id] = max(maxA[id<<1], maxA[id<<1|1]);}
void build(int id, int l, int r){
if(l==r){
maxA[id] = a[l];
return;
}
int mid = (l+r)>>1;
build(id<<1, l, mid);
build(id<<1|1, mid+1, r);
pushup(id);
}
void update(int id, int l, int r, int x, int v){
if(l==r){
maxA[id] = v;
return;
}
int mid = (l+r)>>1;
if(x<=mid) update(id<<1, l, mid, x, v);
else update(id<<1|1, mid+1, r, x, v);
pushup(id);
}
int query(int id, int l, int r, int x, int y){
if(x<=l && y>=r) return maxA[id];
int mid = (l+r)>>1, ret = -inf;
if(x <= mid) ret = max(ret, query(id<<1, l, mid, x, y));
if(y > mid) ret = max(ret, query(id<<1|1, mid+1, r, x, y));
return ret;
}
int main()
{
char opt;
int x, y;
while(scanf("%d%d",&N,&M)!=EOF){
ms(a, 0);
fill(maxA, maxA+maxn, -inf);
for(int i = 1; i <= N; i++)
scanf("%d",&a[i]);
build(1, 1, N);
while(M--){
scanf(" %c%d%d",&opt,&x,&y);
if(opt=='Q')
printf("%d\n",query(1, 1, N, x, y));
else if(opt=='U')
update(1, 1, N, x, y);
}
}
return 0;
}
3.A Simple Problem with Integers
POJ - 3468
思路:模板题,别敲错板子就行(WA好几次2333)
#include
using namespace std;
typedef long long ll;
const ll inf = 4e18+10;
const int mod = 1000000007;
const int mx = 5e6+5;
ll sum[mx], add[mx];
void pushup(int rt) {
sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
void pushdown(int rt, int m) {//更新rt的子节点
if (add[rt]) {
add[rt << 1] += add[rt];
add[rt << 1 | 1] += add[rt];
sum[rt << 1] += (m - (m >> 1))* add[rt];
sum[rt << 1 | 1] += (m >> 1)* add[rt];
add[rt] = 0;//取消本层标记
}
}
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
void biuld(int l, int r, int rt) {//用满二叉树建树
add[rt] = 0;
if (l == r) {//叶子结点,赋值
scanf("%lld", &sum[rt]);
return;
}
int mid = (l + r) >> 1;
biuld(lson);
biuld(rson);
pushup(rt);//向上更新区间和
}
void update(int a, int b, ll c, int l, int r, int rt) {//区间更新
if (a <= l && b >= r) {
sum[rt] += (r - l + 1) * c;
add[rt] += c;
return;
}
pushdown(rt, r - l + 1);//向下更新
int mid = (l + r) >> 1;
if (a <= mid)update(a, b, c, lson);//分成两半深入
if (b > mid)update(a, b, c, rson);
pushup(rt);
}
ll query(int a, int b, int l, int r, int rt) {//区间求和
if (a <= l && b >= r)
return sum[rt];//满足lazy直接返回
pushdown(rt, r - l + 1);
int mid = (l + r) >> 1;
ll ans = 0;
if (a <= mid)ans += query(a, b, lson);
if (b > mid)ans += query(a, b, rson);
return ans;
}
int main()
{
//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int n, m;
scanf("%d", &n); scanf("%d", &m);
biuld(1, n, 1);
while (m--) {
char str[2];
int a, b;
ll c;
scanf("%s", str);
if (str[0] == 'C') {
scanf("%d", &a); scanf("%d", &b); scanf("%lld", &c);
update(a, b, c, 1, n, 1);
}
else {
scanf("%d", &a); scanf("%d", &b);
printf("%lld\n", query(a, b, 1, n, 1));
}
}
return 0;
}
4.Mayor's posters
POJ - 2528
线段树 + 离散化
https://www.cnblogs.com/RioTian/p/13410156.html
5. Just a Hook
HDU - 1698
题意:
N个数, 初始全部为1, 进行Q次以下操作
把[l, r]的所有值改为V (1<=V<=3)
求N个数的和.
题解:
套上线段树区间更新的板子, 把求和的+= 改为 =
#include
using namespace std;
#define sc(n) scanf("%c",&n)
#define sd(n) scanf("%d",&n)
#define pd(n) printf("%d\n", (n))
#define sdd(n,m) scanf("%d %d",&n,&m)
#define sddd(n,m,z) scanf("%d %d %d",&n,&m,&z)
#define pdd(n,m) printf("%d %d\n",n, m)
#define ms(a,b) memset(a,b,sizeof(a))
#define all(c) c.begin(),c.end()
#define pb push_back
#define fi first
#define se second
#define mod(x) ((x)%MOD)
#define lowbit(x) (x & (-x))
#define gcd(a,b) __gcd(a,b)
typedef long long ll;
typedef pair PII;
typedef vector VI;
typedef vector VS;
const int MOD = 1e9 + 7;
const double eps = 1e-9;
const int inf = 0x3f3f3f3f;
const int maxn = 1e5 + 5;
int t, n, m, x, y, z, sum[maxn << 2], ans;
void pushdown(int p) {
if (sum[p] == -1)return;
sum[p << 1] = sum[p << 1 | 1] = sum[p];
sum[p] = -1;
}
void update(int id, int l, int r, int x, int y, int z) {
if (x <= l && r <= y) {
sum[id] = z; return;
}
pushdown(id);
int m = (l + r) >> 1;
if (x <= m)update(id << 1, l, m, x, y, z);
if (y > m) update(id << 1 | 1, m + 1, r, x, y, z);
}
void query(int id, int l, int r) {
if (sum[id] != -1) {
ans += sum[id] * (r - l + 1);
return;
}
int mid = (l + r) >> 1;
query(id << 1, l, mid);
query(id << 1 | 1, mid + 1, r);
}
int main() {
//freopen("in.txt", "r", stdin);
//ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
int Kcase = 1;
sd(t); while (t--) {
ans = 0; ms(sum, -1);
sdd(n, m); sum[1] = 1;
while (m--) {
sddd(x, y, z);
update(1, 1, n, x, y, z);
}
query(1, 1, n);
printf("Case %d: The total value of the hook is %d.\n", Kcase++, ans);
}
return 0;
}
6.Count the Colors
ZOJ - 1610
题解: https://www.cnblogs.com/RioTian/p/13410855.html
7. Balanced Lineup
POJ - 3264
搭配注释理解
#include
#include
using namespace std;
typedef pair PII;
const int N = 50000 + 10, M = N * 4;
struct Node {
// 不用开long long
int l, r, hei, low;
} root[M];
void pushup(int now) {
// 记录最高的和最矮的牛的高度
root[now].hei = max(root[now << 1].hei, root[now << 1 | 1].hei);
root[now].low = min(root[now << 1].low, root[now << 1 | 1].low);
}
void build(int now, int left, int right) {
root[now].l = left, root[now].r = right;
if (left == right) {
// 直接输入叶子节点的值,很方便,而且省空间
scanf("%d", &root[now].hei);
// 默认叶子节点的牛既是最高的,也是最矮的
root[now].low = root[now].hei;
return;
}
int mid = (left + right) >> 1;
int ln = now << 1, rn = now << 1 | 1;
build(ln, left, mid);
build(rn, mid + 1, right);
pushup(now);
}
// 甚至不用写update和pushdown...
PII query(int now, int L, int R) {
// 找到了要的区间返回这个区间的最大值和最小值,用来和后面的区间进行对比
if (L <= root[now].l && root[now].r <= R) return PII(root[now].hei, root[now].low);
// 规定first为最大值,second为最小值,那么没找到就返回一个极端值就行了
if (L > root[now].r || R < root[now].l) return PII(-1e9, 1e9);
// 不用pushdown
// 找到左右子树的最大值和最小值对,然后对比
PII nhei = query(now << 1, L, R);
PII nlow = query(now << 1 | 1, L ,R);
// 对比左区间的最大值和右区间的最大值,左区间的最小值和右区间的最小值...
return PII(max(nhei.first, nlow.first), min(nhei.second, nlow.second));
}
int main() {
int n, m, l ,r;
scanf("%d%d", &n, &m);
build(1, 1, n);
while (m--) {
scanf("%d%d", &l, &r);
PII res = query(1, l, r);
printf("%d\n", res.first - res.second);
}
return 0;
}
8. Can you answer these queries?
HDU - 4027
线段树,保留有用的功能即可。
#include
typedef long long ll;
#define mid (l+r)/2
#define lch in*2
#define rch in*2+1
const int maxn = 1e5 + 9;
int N, M, L, R;
ll tr[maxn * 4] = {};
void build(int in = 1, int l = 1, int r = N) {
if (l == r) return void(scanf("%lld", tr + in));
build(lch, l, mid); build(rch, mid + 1, r);
tr[in] = tr[lch] + tr[rch];
}
void update(int in = 1, int l = 1, int r = N) {
if (l > R || r < L || tr[in] == (r - l + 1)) return;
if (l == r) return void(tr[in] = sqrt(tr[in]));
update(lch, l, mid); update(rch, mid + 1, r);
tr[in] = tr[lch] + tr[rch];
}
ll qurry(int in = 1, int l = 1, int r = N) {
if (l > R || r < L) return 0;
if (L <= l && R >= r) return tr[in];
return qurry(lch, l, mid) + qurry(rch, mid + 1, r);
}
void solve() {
build();
scanf("%d", &M);
while (M--) {
int t; scanf("%d%d%d", &t, &L, &R);
if (L > R) L ^= R ^= L ^= R;
if (!t) update();
else printf("%lld\n", qurry());
}
}
int main() {
//freopen("in.txt", "r", stdin);
for (int __ = 1; ~scanf("%d", &N);) {
printf("Case #%d:\n", __++);
solve();
puts("");
}
}