kuangbin专题七 线段树

kuangbin专题

1. 敌兵布阵 HDU - 1166

难度: ★【究极入门题,纯当是检验板子】

题意: 中文题面,不再赘述

题解: 参考线段树&树状数组算法,有板子就是直接AC。

因为是回头刚整理线段树,所以搞了个不一定全面当时非常长的模板,凑合着用

#include<bits/stdc++.h>
using namespace std;

const int inf = 0x3f3f3f3f;
const int maxn = 1e5 + 10;
#define lrt rt << 1
#define rrt rt << 1 | 1
#define lson l, mid, lrt
#define rson mid + 1, r, rrt

#define _rep(i,a,n) for (int i = (a); i <= (n); ++i)

struct node {
	int l, r;
	int sum, max, lazy;
	int mid () { return (l + r) >> 1; }
	void set (int l, int r, int lazy = 0) { this->l = l, this->r = r, this->lazy = lazy; }
	node () { l = r = sum = max = lazy = 0; }
	node (int l, int r, int s, int m, int la) : l(l), r(r), sum(s), max(m), lazy(la) {}
} ;

int n, k, c;
int a[maxn];
char str[7];
node T[maxn << 2]; 

inline void pushup (int rt) {
	T[rt].sum = T[lrt].sum + T[rrt].sum;
	T[rt].max = max(T[lrt].max, T[rrt].max);
}

inline void pushdown (int rt) {
	if (T[rt].lazy == 0) return ;
	
	T[lrt].lazy += T[rt].lazy;
	T[lrt].sum += T[rt].lazy * (T[lrt].r - T[lrt].l + 1);
	T[lrt].max += T[rt].lazy;
	
	T[rrt].lazy += T[rt].lazy;
	T[rrt].sum += T[rt].lazy * (T[rrt].r - T[rrt].l + 1);
	T[rrt].max += T[rt].lazy;
	
	T[rt].lazy = 0;
}

void build (int l, int r, int rt) {
	T[rt].l = l, T[rt].r = r, T[rt].lazy = 0;
	if (l == r) return(void)( T[rt].sum = T[rt].max = a[l] );
	int mid = T[rt].mid();
	build(lson);
	build(rson);
	pushup(rt);
}

void update (int c, int l, int r, int rt) {
	if (T[rt].l >= l && T[rt].r <= r) return (void) (
		T[rt].lazy += c,
		T[rt].sum += c * (T[rt].r - T[rt].l + 1),
		T[rt].max += c
	);
	pushdown(rt);
	int mid = T[rt].mid();
	if (l <= mid) update(c, l, r, lrt);
	if (r > mid)  update(c, l, r, rrt);
	pushup(rt);
}

int querySum (int l, int r, int rt) {
	if (T[rt].l >= l && T[rt].r <= r) return T[rt].sum;
	pushdown(rt);
	int mid = T[rt].mid();
	int sum = 0;
	if (l <= mid) sum += querySum(l, r, lrt);
	if (r >  mid) sum += querySum(l, r, rrt);
	return sum;
}

int queryMax (int l, int r, int rt) {
	if (T[rt].l >= l && T[rt].r <= r) return T[rt].max;
	pushdown(rt);
	int mid = T[rt].mid();
	int maxx = -inf;
	if (l <= mid) maxx = max(maxx, queryMax(l, r, lrt));
	if (r >  mid) maxx = max(maxx, queryMax(l, r, rrt));
	return maxx;
}

int cas;
void run () {
	scanf("%d", &n);
	_rep (i, 1, n) scanf("%d", a + i);
	build(1, n, 1);
	
	printf("Case %d:\n", ++cas);
	while (~scanf("%s", str)) {
		if (str[0] == 'E') break;
		scanf("%d%d", &k, &c);
		if (str[0] == 'A') update(c, k, k, 1);
		if (str[0] == 'S') update(-c, k, k, 1);
		if (str[0] == 'Q') printf("%d\n", querySum(k, c, 1));
	}
}

int main () {
	int _T;
	scanf("%d", &_T);
	while (_T--) run();
}

2. I Hate It HDU - 1754

难度: ★【入门题】

题意: 中文题面不加赘述

题解: 检验模板题,用到单点修改和区间查询。

刚开始整理,比较粗糙的模板,不一定全面还比较冗长,见谅

#include<bits/stdc++.h>
using namespace std;

const int inf = 0x3f3f3f3f;
const int maxn = 2e5 + 10;
#define lrt rt << 1
#define rrt rt << 1 | 1
#define lson l, mid, lrt
#define rson mid + 1, r, rrt

#define _rep(i,a,n) for (int i = (a); i <= (n); ++i)

struct node {
	int l, r;
	int sum, max, lazy;
	int mid () { return (l + r) >> 1; }
	void set (int l, int r, int lazy = 0) { this->l = l, this->r = r, this->lazy = lazy; }
	node () { l = r = sum = max = lazy = 0; }
	node (int l, int r, int s, int m, int la) : l(l), r(r), sum(s), max(m), lazy(la) {}
} ;

int n, m, k, c;
int a[maxn];
char str[7];
node T[maxn << 2]; 

inline void pushup (int rt) {
	T[rt].sum = T[lrt].sum + T[rrt].sum;
	T[rt].max = max(T[lrt].max, T[rrt].max);
}

inline void pushdown (int rt) {
	if (T[rt].lazy == 0) return ;
	
	T[lrt].lazy += T[rt].lazy;
	T[lrt].sum += T[rt].lazy * (T[lrt].r - T[lrt].l + 1);
	T[lrt].max += T[rt].lazy;
	
	T[rrt].lazy += T[rt].lazy;
	T[rrt].sum += T[rt].lazy * (T[rrt].r - T[rrt].l + 1);
	T[rrt].max += T[rt].lazy;
	
	T[rt].lazy = 0;
}

void build (int l, int r, int rt) {
	T[rt].l = l, T[rt].r = r, T[rt].lazy = 0;
	if (l == r) return(void)( T[rt].sum = T[rt].max = a[l] );
	int mid = T[rt].mid();
	build(lson);
	build(rson);
	pushup(rt);
}

void change (int c, int k, int rt) {
	if (T[rt].l == T[rt].r && T[rt].l == k) return (void) (
		T[rt].sum = c,
		T[rt].max = c
	);
	int mid = T[rt].mid();
	if (k <= mid) change(c, k, lrt);
	else change(c, k, rrt);
	pushup(rt);
}

void update (int c, int l, int r, int rt) {
	if (T[rt].l >= l && T[rt].r <= r) return (void) (
		T[rt].lazy += c,
		T[rt].sum += c * (T[rt].r - T[rt].l + 1),
		T[rt].max += c
	);
	pushdown(rt);
	int mid = T[rt].mid();
	if (l <= mid) update(c, l, r, lrt);
	if (r > mid)  update(c, l, r, rrt);
	pushup(rt);
}

int querySum (int l, int r, int rt) {
	if (T[rt].l >= l && T[rt].r <= r) return T[rt].sum;
	pushdown(rt);
	int mid = T[rt].mid();
	int sum = 0;
	if (l <= mid) sum += querySum(l, r, lrt);
	if (r >  mid) sum += querySum(l, r, rrt);
	return sum;
}

int queryMax (int l, int r, int rt) {
	if (T[rt].l >= l && T[rt].r <= r) return T[rt].max;
	pushdown(rt);
	int mid = T[rt].mid();
	int maxx = -inf;
	if (l <= mid) maxx = max(maxx, queryMax(l, r, lrt));
	if (r >  mid) maxx = max(maxx, queryMax(l, r, rrt));
	return maxx;
}

void run () {
	scanf("%s%d%d", str, &k, &c);
	if (str[0] == 'U') return (void)(change(c, k, 1));
	printf("%d\n", queryMax(k, c, 1));
}

int main () {
	while (~scanf("%d%d", &n, &m)) {
		_rep (i, 1, n) scanf("%d", a + i);
		build(1, n, 1);
		while (m--) run();	
	}
}

3. A Simple Problem with Integers POJ - 3468

难度: ★【入门题】

题意: 区间修改和区间查询

题解: 考察线段树的区间修改和区间查询

#include<iostream>
using namespace std;

const int maxn = 1e5 + 10;
#define lrt rt << 1
#define rrt rt << 1 | 1
#define lson l, mid, lrt
#define rson mid + 1, r, rrt
#define ll long long
#define _rep(i,a,n) for (int i = (a); i <= (n); ++i)

struct node {
	int l, r;
	ll lazy, sum;
	int mid () { return (l + r) >> 1; }
	node () { l = r = lazy = sum = 0; }
}; 

int n, m;
ll a[maxn];
node T[maxn << 2];

inline void pushup (int rt) { T[rt].sum = T[lrt].sum + T[rrt].sum; }
inline void pushdown (int rt) {
	if (T[rt].lazy == 0) return ;
	T[lrt].lazy += T[rt].lazy;
	T[rrt].lazy += T[rt].lazy;
	T[lrt].sum += T[rt].lazy * (T[lrt].r - T[lrt].l + 1);
	T[rrt].sum += T[rt].lazy * (T[rrt].r - T[rrt].l + 1);
	T[rt].lazy = 0;
}

void build (int l, int r, int rt) {
	T[rt].l = l, T[rt].r = r, T[rt].lazy = 0;
	if (l == r) return (void)(T[rt].sum = a[l]);
	int mid = T[rt].mid();
	build(lson);
	build(rson);
	pushup(rt);
}

void update (ll c, int l, int r, int rt) {
	if (T[rt].l >= l && T[rt].r <= r) return (void) (
		T[rt].lazy += c,
		T[rt].sum += c * (T[rt].r - T[rt].l + 1)
	);
	pushdown(rt);
	int mid = T[rt].mid();
	if (l <= mid) update(c, l, r, lrt);
	if (r >  mid) update(c, l, r, rrt);
	pushup(rt);
}

ll query (int l, int r, int rt) {
	if (T[rt].l >= l && T[rt].r <= r) return T[rt].sum;
	pushdown(rt);
	int mid = T[rt].mid();
	ll res = 0;
	if (l <= mid) res += query(l, r, lrt);
	if (r >  mid) res += query(l, r, rrt);
	return res;
}

void run () {
	_rep (i, 1, n) scanf("%lld", a + i);
	build(1, n, 1);
	while (m--) {
		int l, r, c;
		char str[2];
		scanf("%s%d%d", str, &l, &r);
		if (str[0] == 'Q') printf("%lld\n", query(l, r, 1));
		else {
			scanf("%d", &c);
			update((ll)c, l, r, 1);
		}
	}
}

int main () {
	while (~scanf("%d%d", &n, &m)) run();
}

4. Mayor’s posters POJ - 2528

难度: ★★

题意: 在一块广告牌上贴很多的广告【相同位置会覆盖上去】,问最后一共能看到多少广告

题解: 每个位置不同广告则进行不同的染色。问题在于这个题目的区间非常大,需要进行离散化。光是一般的离散化还不够,比如离散化之后,{1, 6}、{1,3}和{4,6}分别有三块广告牌,肉眼分析后两张广告会完全覆盖掉第一张广告的,但是这样的数据去查询区间[3,4]会告诉你是第一张广告牌。因此离散化的时候还要特别处理这个问题。

  • 处理办法是如果{1,3}上有心的广告,则对线段树的区间{1,4}进行更新,就是要覆盖多一个单位。
  • 那么进行离散化的时候,就要多预留这样的一个单位
#include<iostream>
#include<algorithm>
#include<set>
using namespace std;

#define _for(i,a,n) for (int i = (a); i <  (n); ++i)
#define _rep(i,a,n) for (int i = (a); i <= (n); ++i)
#define lrt rt << 1
#define rrt rt << 1 | 1
#define lson l, mid, lrt
#define rson mid + 1, r, rrt

const int maxn = 1e5 + 10;

struct node {
	int l, r, col;
	int mid () { return l + r >> 1; }
	node () { l = r = col = 0; }
};

int _T, n, cnt;
int len[maxn];
int l[maxn];
int r[maxn];
node T[maxn << 2];

void build (int l, int r, int rt) {
	T[rt].l = l, T[rt].r = r;
	if (l == r) return ;
	int mid = T[rt].mid();
	build(lson);
	build(rson);
}

void pushdown (int rt) {
	if (T[rt].col == 0) return ;
	T[lrt].col = T[rt].col;
	T[rrt].col = T[rt].col;
	T[rt].col  = 0;
}

void update (int col, int l, int r, int rt) {
	if (T[rt].l >= l && T[rt].r <= r) return (void)( T[rt].col = col );
	pushdown(rt);
	int mid = T[rt].mid();
	if (l <= mid) update(col, l, r, lrt);
	if (r >  mid) update(col, l, r, rrt);
}

int query (int k, int rt) {
	if (T[rt].l == T[rt].r && T[rt].l == k) return T[rt].col;
	pushdown(rt);
	int mid = T[rt].mid();
	if (k <= mid) return query(k, lrt);
	return query(k, rrt);
}

void run () {
	cnt = 0;
	scanf("%d", &n);
	_rep (i, 1, n) {
		scanf("%d%d", l + i, r + i);
		len[++cnt] = l[i];
		len[++cnt] = r[i];
	}
	sort(len + 1, len + cnt + 1);
	int tem = cnt = unique(len + 1, len + cnt + 1) - len - 1;
	_rep (i, 2, tem) if (len[i] - len[i - 1] > 1) len[++cnt] = len[i] - 1;
	sort(len + 1, len + cnt + 1);
	
	build(1, cnt, 1);
	_rep (i, 1, n) {
		int powl = lower_bound(len + 1, len + cnt + 1, l[i]) - len;
		int powr = lower_bound(len + 1, len + cnt + 1, r[i]) - len;
		update(i, powl, powr, 1);
	}
	set<int> st;
	_rep (i, 1, cnt) {
		int col = query(i, 1);
		if (col) st.insert(col);
	}
	printf("%d\n", st.size());
}

int main () {
	scanf("%d", &_T);
	while (_T--) run();
	return 0;
}

5. Just a Hook HDU - 1698

难度: ★【入门题】

题意: 一个长度为 n n n的钩子,其中每一段的权值为1,每次将某个区间的所有值修改为 k k k,最后问整个钩子的权值和。

题解: 区间修改的模板题【有些区间修改是增量的,这个是直接改变成某值】

#include<bits/stdc++.h>
using namespace std;

const int inf = 0x3f3f3f3f;
const int maxn = 2e5 + 10;
#define lrt rt << 1
#define rrt rt << 1 | 1
#define lson l, mid, lrt
#define rson mid + 1, r, rrt
#define _rep(i,a,n) for (int i = (a); i <= (n); ++i)

struct node {
	int l, r;
	int sum, lazy;
	int mid () { return (l + r) >> 1; }
	node () { l = r = sum = lazy = 0; }
	node (int l, int r, int s, int la) : l(l), r(r), sum(s), lazy(la) {}
} ;

int n, m;
int a[maxn];
char str[7];
node T[maxn << 2]; 

inline void pushup (int rt) {
	T[rt].sum = T[lrt].sum + T[rrt].sum;
}

inline void pushdown (int rt) {
	if (T[rt].lazy == 0) return ;
	T[lrt].lazy = T[rt].lazy;
	T[lrt].sum = T[rt].lazy * (T[lrt].r - T[lrt].l + 1);
	T[rrt].lazy = T[rt].lazy;
	T[rrt].sum = T[rt].lazy * (T[rrt].r - T[rrt].l + 1);
	T[rt].lazy = 0;
}

void build (int l, int r, int rt) {
	T[rt].l = l, T[rt].r = r, T[rt].lazy = 0;
	if (l == r) return(void)( T[rt].sum = a[l] );
	int mid = T[rt].mid();
	build(lson);
	build(rson);
	pushup(rt);
}

void change (int c, int l, int r, int rt) {
	if (T[rt].l >= l && T[rt].r <= r) return (void)(
		T[rt].lazy = c,
		T[rt].sum = c * (T[rt].r - T[rt].l + 1)
	);
	pushdown(rt);
	int mid = T[rt].mid();
	if (l <= mid) change(c, l, r, lrt);
	if (r >  mid) change(c, l, r, rrt);
	pushup(rt);
}

void run (int cas) {
	scanf("%d", &n);
	_rep (i, 1, n) a[i] = 1;
	build(1, n, 1);
	
	scanf("%d", &m);
	while (m--) {
		int l, r, c;
		scanf("%d%d%d", &l, &r, &c);
		change(c, l, r, 1);
	}
	printf("Case %d: The total value of the hook is %d.\n", cas, T[1].sum);
}

int main () {
	int _T;
	scanf("%d", &_T);
	_rep (i, 1, _T) run(i);
}

7. Balanced Lineup POJ - 3264

难度:

题意: 查询区间内的最大差值

题解: 只有区间查询,写两个函数,一个查询区间最大值,一个查询区间最小值,减一下既得最大差值

#include<iostream>
#include<algorithm>
using namespace std;

const int inf = 0x3f3f3f3f;
const int maxn = 5e4 + 10;
#define lrt rt << 1
#define rrt rt << 1 | 1
#define lson l, mid, lrt
#define rson mid + 1, r, rrt
#define _rep(i,a,n) for (int i = (a); i <= (n); ++i)

struct node {
	int l, r, min, max;
	int mid () { return l + r >> 1; }
	node () { l = r = max = 0, min = inf; }
};

int n, m, l, r;
int a[maxn];
node T[maxn << 2];

inline void pushup (int rt) {
	T[rt].max = max(T[lrt].max, T[rrt].max);
	T[rt].min = min(T[lrt].min, T[rrt].min);
}

void build (int l, int r, int rt) {
	T[rt].l = l, T[rt].r = r;
	if (l == r) return (void)(T[rt].max = T[rt].min = a[l]);
	int mid = T[rt].mid();
	build(lson);
	build(rson);
	pushup(rt);
}

int queryMax (int l, int r, int rt) {
	if (T[rt].l >= l && T[rt].r <= r) return T[rt].max;
	int mid = T[rt].mid();
	int res = 0;
	if (l <= mid) res = max(res, queryMax(l, r, lrt));
	if (r >  mid) res = max(res, queryMax(l, r, rrt));
	return res;
}

int queryMin (int l, int r, int rt) {
	if (T[rt].l >= l && T[rt].r <= r) return T[rt].min;
	int mid = T[rt].mid();
	int res = inf;
	if (l <= mid) res = min(res, queryMin(l, r, lrt));
	if (r >  mid) res = min(res, queryMin(l, r, rrt));
	return res;
}

void run () {
	_rep (i, 1, n) scanf("%d", a + i);
	build(1, n, 1);
	while (m--) {
		scanf("%d%d", &l, &r);
		printf("%d\n", queryMax(l, r, 1) - queryMin(l, r, 1));
	}
}

int main () {
	while (~scanf("%d%d", &n, &m)) run();	
}

8. Can you answer these queries? HDU - 4027

难度:

题意: [ 1 , n ] [1,n] [1,n]区间有一个好感度,每次操作会对一个区间内的所有好感度进行开平方根处理,查询则是对一个区间的求和。

题解: 考察线段树的区间修改和区间查询。区别在于区间修改部分,因为是对每一个好感度进行开平方根,所以原则上应该是落实到单点修改的。但是有一个细节在于比方说我某个区间的所有好感度早就开根到1了,接下来无论怎么开根都不会变化,主要针对这里进行优化。

#include<bits/stdc++.h>
using namespace std;

typedef long long ll;
const int maxn = 1e5 + 10;
#define lrt rt << 1
#define rrt rt << 1 | 1

struct node {
	int l, r;
	ll sum;
	int mid () { return l + r >> 1; }
	node () { l = r = sum = 0; }
};

int n, m;
ll a[maxn];
node T[maxn << 2];

void pushup (int rt) { T[rt].sum = T[lrt].sum + T[rrt].sum; }

void build (int l, int r, int rt) {
	T[rt].l = l, T[rt].r = r;
	if (l == r) return (void)(T[rt].sum = a[l]);
	int mid = T[rt].mid();
	build(l, mid, lrt);
	build(mid + 1, r, rrt);
	pushup(rt);
}

void update (int l, int r, int rt) {
	if (T[rt].sum == T[rt].r - T[rt].l + 1) return ;
	if (T[rt].l == T[rt].r) return (void)(T[rt].sum = sqrt(T[rt].sum));
	int mid = T[rt].mid();
	if (l <= mid) update(l, r, lrt);
	if (r >  mid) update(l, r, rrt);
	pushup(rt);
}

ll query (int l, int r, int rt) {
	if (T[rt].l >= l && T[rt].r <= r) return T[rt].sum;
	int mid = T[rt].mid();
	ll res = 0;
	if (l <= mid) res += query(l, r, lrt);
	if (r >  mid) res += query(l, r, rrt);
	return res;
}

void run () {
	for (int i = 1; i <= n; ++i) scanf("%lld", a + i);
	build(1, n, 1);
	scanf("%d", &m);
	while (m--) {
		int k, l, r;
		scanf("%d%d%d", &k, &l, &r);
		if (l > r) swap(l, r);
		if (k == 0) update(l, r, 1);
		else printf("%lld\n", query(l, r, 1));
	}
}

int cas;
int main () {
	while (~scanf("%d", &n)) {
		printf("Case #%d:\n", ++cas);
		run();
		puts("");
	}
	return 0;
}

9. Tunnel Warfare HDU - 1540

难度: ★★

题意: n n n个村庄排成一列,相邻的两个村庄表示相互连接。 D i Di Di表示摧毁第 i i i个村庄,并且切断了与其他村庄的联系; Q i Qi Qi表示询问能与第 i i i个村庄联系的村庄数; R R R表示恢复上一个被摧毁的村庄。

题解: 不同于求和和取最大值,这个关乎到了连续的区间。因此需要在线段树中记录两个值, L m a x Lmax Lmax表示这个区间前段连续的长度, R m a x Rmax Rmax表示这个区间后段连续的长度

#include<bits/stdc++.h>
using namespace std;

const int maxn = 5e4 + 10;
#define lrt rt << 1
#define rrt rt << 1 | 1

struct node {
	int l, r;
	int Lmax, Rmax;
	int mid () { return l + r >> 1; }
};

int n, m;
int a[maxn];
node T[maxn << 2];
stack<int> st;

void pushup (int rt) {
	node L = T[lrt], R = T[rrt];
	T[rt].Lmax = L.Lmax + (L.Lmax == L.r - L.l + 1 ? R.Lmax : 0);
	T[rt].Rmax = R.Rmax + (R.Rmax == R.r - R.l + 1 ? L.Rmax : 0);
}

void build (int l, int r, int rt) {
	T[rt].l = l, T[rt].r = r;
	T[rt].Lmax = T[rt].Rmax = r - l + 1;
	if (l == r) return ;
	int mid = T[rt].mid();
	build(l, mid, lrt);
	build(mid + 1, r, rrt);
}

void update (int c, int k, int rt) {
	if (T[rt].l == T[rt].r && T[rt].l == k) return (void)(
		T[rt].Lmax = T[rt].Rmax = c
	);
	int mid = T[rt].mid();
	if (k <= mid) update(c, k, lrt);
	else		  update(c, k, rrt);
	pushup(rt);
}

int query (int k, int rt) {
	int mid = T[rt].mid();
	if (k <= mid) {
		if (k >= T[lrt].r - T[lrt].Rmax + 1) return T[lrt].Rmax + T[rrt].Lmax;
		return query(k, lrt);
	}
	if (k <= T[rrt].l + T[rrt].Lmax - 1) return T[lrt].Rmax + T[rrt].Lmax;
	return query(k, rrt);
}

void run () {
	int k;
	char s[10];
	scanf("%s", s);
	if (s[0] == 'R') {
		if (st.empty()) return;
		update(1, st.top(), 1);
		st.pop();
	} else {
		scanf("%d", &k);
		if (s[0] == 'Q') printf("%d\n", query(k, 1));
		else {
			st.push(k);
			update(0, k, 1);
		}
	}
}

int main () {
	while (~scanf("%d%d", &n, &m)) {
		build(1, n, 1);
		while (st.size()) st.pop();
		while (m--) run();
	}
	return 0;
}

10. Assign the task HDU - 3974

难度: ★★

题意: 有一棵代表上下级关系的树,给某个节点发布的任务,会下发到这个节点下的所有节点。询问的是单个点当前的任务。

题解: 任务的发布和询问只是一个染色问题而已,只是常规的线段树。但是题目的输入需要一波处理,因为给的是一些节点的关系。因为题目保证是一棵有根树,找到根节点之后DFS对每个节点进行一个区间的规划,然后就是常规的线段树部分。

#include<bits/stdc++.h>
using namespace std;

const int maxn = 5e4 + 10;
#define lrt rt << 1
#define rrt rt << 1 | 1
#define _for(i,a,n) for (int i = (a); i <  (n); ++i)
#define _rep(i,a,n) for (int i = (a); i <= (n); ++i)

struct node {
	int l, r, color;
	int mid () { return l + r >> 1; }
	node () { color = -1; }
};

int _T, n, m, cnt;
int vis[maxn];
int mapL[maxn];
int mapR[maxn];
node T[maxn << 3];
vector<int> G[maxn];

void dfs (int u) {
	mapL[u] = ++cnt;
	for (auto v : G[u]) dfs(v);
	mapR[u] = ++cnt;
}

void build (int l, int r, int rt) {
	T[rt].l = l, T[rt].r = r, T[rt].color = -1;
	if (l == r)	  return ;
	int mid = T[rt].mid();
	build(l, mid, lrt);
	build(mid + 1, r, rrt);
}

void pushdown (int rt) {
	if (T[rt].color == -1) return;
	T[lrt].color = T[rt].color;
	T[rrt].color = T[rt].color;
	T[rt].color = -1;	
}

void update (int c, int l, int r, int rt) {
	if (T[rt].l >= l && T[rt].r <= r) return (void)( T[rt].color = c );
	pushdown(rt);
	int mid = T[rt].mid();
	if (l <= mid) update(c, l, r, lrt);
	if (r >  mid) update(c, l, r, rrt);
}

int query (int k, int rt) {
	if (T[rt].l == T[rt].r && T[rt].l == k) return T[rt].color;
	if (T[rt].color != -1) return T[rt].color;
	int mid = T[rt].mid();
	if (k <= mid) return query(k, lrt);
	return query(k, rrt);
}

int cas;
void run () {
	printf("Case #%d:\n", ++cas);
	
	cnt = 0;
	memset(vis,  0, sizeof vis);
	memset(mapL, 0, sizeof mapL);
	memset(mapR, 0, sizeof mapR);
	_for (i, 0, maxn) G[i].clear();
	
	scanf("%d", &n);

	build(1, n << 1, 1);
	
	_for (i, 1, n) {
		int u, v;
		scanf("%d%d", &u, &v);
		G[v].push_back(u);
		vis[u] = 1;
	}
	_rep (i, 1, n) if (!vis[i]) dfs(i);
	
	scanf("%d", &m);
	while (m--) {
		char s[10];
		int x, y;
		scanf("%s%d", s, &x);
		if (s[0] == 'C') printf("%d\n", query(mapL[x], 1));
		else {
			scanf("%d", &y);
			update(y, mapL[x], mapR[x], 1);
		}
	}
}

int main () {
	scanf("%d", &_T);
	while (_T--) run();
	return 0;
}

11. Transformation HDU - 4578

难度: ★★

题意: 很长的题面——狄仁杰断案,我和元芳一样懵。本质上就是三种区间操作和一个区间查询,一种是区间内所有数 + c +c +c,一种是区间内所有数 ∗ c *c c,最后一种是区间内所有数 = c =c =c。查询也有一些特殊,求的是所有数的k次幂的和。

题解: 线段树中记录两个内容: v a l val val记录值, f l a g = 1 flag = 1 flag=1表示这个区间内所有节点的值相同。向下查询的时候,到某个区间发现 f l a g = 1 flag = 1 flag=1则不需要再向下查询,调用val即可。区间更新的时候,也要特别地对flag进行更新。

#include<bits/stdc++.h>
using namespace std;

const int maxn = 1e5 + 7;
const int mod  = 10007;
#define lrt rt << 1
#define rrt rt << 1 | 1

struct node {
	int l, r;
	int flag, val;
	int mid () { return l + r >> 1;	}
	node () { flag = 1, val = 0; }
};

int n, m;
node T[maxn << 2];

void pushup (int rt) {
	if (!T[lrt].flag || !T[rrt].flag) return (void)(T[rt].flag = 0);
	if (T[lrt].val != T[rrt].val) return (void)(T[rt].flag = 0);
	T[rt].flag = 1, T[rt].val = T[lrt].val;
}

void pushdown (int rt) {
	if (T[rt].flag == 0) return ;
	T[lrt].val = T[rrt].val = T[rt].val;
	T[rt].flag = 0;
}

void build (int l, int r, int rt) {
	T[rt].l = l, T[rt].r = r, T[rt].flag = 1, T[rt].val = 0;
	if (l == r) return ;
	int mid = T[rt].mid();
	build(l, mid, lrt);
	build(mid + 1, r, rrt);
}

void update (int op, int c, int l, int r, int rt) {
	if (T[rt].l >= l && T[rt].r <= r && T[rt].flag) {
		if (op == 1) T[rt].val += c;
		if (op == 2) T[rt].val  = (T[rt].val * c) % mod;
		if (op == 3) T[rt].val  = c;
		return ;
	}
	pushdown(rt);
	int mid = T[rt].mid();
	if (l <= mid) update(op, c, l, r, lrt);
	if (r >  mid) update(op, c, l, r, rrt);
	pushup(rt);
}

int query (int k, int l, int r, int rt) {
	if (T[rt].l >= l && T[rt].r <= r && T[rt].flag) {
		int ans = 1;
		while (k--) ans = (ans * T[rt].val) % mod;
		ans = (ans * (T[rt].r - T[rt].l + 1)) % mod;
		return ans; 
	}
	pushdown(rt);
	int mid = T[rt].mid();
	int ans = 0;
	if (l <= mid) ans += query(k, l, r, lrt);
	if (r >  mid) ans += query(k, l, r, rrt);
	return ans % mod;
}

void run () {
	build(1, n, 1);
	
	while (m--) {
		int op, x, y, c;
		scanf("%d%d%d%d", &op, &x, &y, &c);
		if (op == 4) printf("%d\n", query(c, x, y, 1));
		else update(op, c, x, y, 1);
	}
}

int main () {
	while (~scanf("%d%d", &n, &m) && (n + m)) run();
	return 0;
}

12. Vases and Flowers HDU - 4614

难度: ★★

题意: n n n个花瓶,初始时都为空,每个花瓶最多放一朵花。一种操作为从第 i i i个花瓶开始,一共放入 k k k多花,并输出插入花的第一个花瓶和最后一个花瓶;另一种操作将某个区间内的所有花全部取出,并输出花的数量。

题解: 对于插入时第一个花瓶和最后一个花瓶,则用二分去查找。线段树则用来记录每个区间的花的数量。
关于二分的细节:

  • search(int res, int l, int r, int rt)函数用来查询区间 [ l , r ] [l,r] [l,r]插入 r e s res res朵花的最后一个花瓶的位置。
  • 如果左半区间能够插入 r e s res res朵花,则带 r e s res res朵花向左半区间转移
  • 如果左半区间只能插入 t e m tem tem朵花,则带 r e s − t e m res - tem restem朵花向右半区间转移
  • 直到叶子节点,则是最后一个花瓶的位置
#include<bits/stdc++.h>
using namespace std;

const int maxn = 5e4 + 7;
#define lrt rt << 1
#define rrt rt << 1 | 1

struct node {
	int l, r, sum;
	int mid () { return l + r >> 1; }
};

int _T, n, m;
int ansL, ansR;
node T[maxn << 2];

void build (int l, int r, int rt) {
	T[rt].l = l, T[rt].r = r, T[rt].sum = 0;
	if (l == r) return ;
	int mid = T[rt].mid();
	build(l, mid, lrt);
	build(mid + 1, r, rrt);
}

void pushup (int rt) { T[rt].sum = T[lrt].sum + T[rrt].sum; }

void pushdown (int rt) {
	if (T[rt].sum == T[rt].r - T[rt].l + 1) {
		T[lrt].sum = T[lrt].r - T[lrt].l + 1;
		T[rrt].sum = T[rt].sum - T[lrt].sum;
	}
	if (T[rt].sum == 0) T[lrt].sum = T[rrt].sum = 0;
}

void update (int c, int l, int r, int rt) {
	if (T[rt].l >= l && T[rt].r <= r) return (void)(
		T[rt].sum = (T[rt].r - T[rt].l + 1) * c
	);
	pushdown(rt);
	int mid = T[rt].mid();
	if (l <= mid) update(c, l, r, lrt);
	if (r >  mid) update(c, l, r, rrt);
	pushup(rt);
}

int query (int l, int r, int rt) {
	if (T[rt].l >= l && T[rt].r <= r) return T[rt].sum;
	pushdown(rt);
	int mid = T[rt].mid();
	int res = 0;
	if (l <= mid) res += query(l, r, lrt);
	if (r >  mid) res += query(l, r, rrt);
	return res;
}

int search (int res, int l, int r, int rt) {
	if (l == r) return l;
	pushdown(rt);
	int mid  = T[rt].mid();
	int resL = (T[lrt].r - T[lrt].l + 1) - T[lrt].sum;
	if (res <= resL) return search(res, l, mid, lrt);
	return search(res - resL, mid + 1, r, rrt);
}

void run () {
	scanf("%d%d", &n, &m);
	build(1, n, 1);	
	while (m--) {
		int k, x, y;
		scanf("%d%d%d", &k, &x, &y);
		if (k == 1) {
			x++;
			int tem = n - x + 1 - query(x, n, 1);
			if (tem == 0) printf("Can not put any one.\n");
			else {
				y = min(y, tem);
				int num = (x > 1 ? x - 1 - query(1, x - 1, 1) : 0);
				int bg = search(num + 1, 1, n, 1);
				int ed = search(num + y, 1, n, 1);
				printf("%d %d\n", bg - 1, ed - 1);
				update(1, bg, ed, 1);
			}
		} else {
			x++, y++;
			printf("%d\n", query(x, y, 1));
			update(0, x, y, 1);
		}
	}	
	puts("");
}

int main () {
	scanf("%d", &_T);
	while (_T--) run();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值