目录
- 1. hdu1166 敌兵布阵
- 2. hdu 1754 I Hate It
- 3. poj 3468 A Simple Problem with Integers
- 4. poj 2528 Mayor's posters
- 5. hdu 1498 Just a Hook
- 6. zoj 1610 Count the Colors
- 7. poj 3264 Balanced Lineup
- 8. hdu 4027 Can you answer these queries?
- 9. hdu 1540 Tunnel Warfare
- 10. hdu 3974 Assign the task
- 12. hdu 4614 Vases and Flowers
- 14. poj 1177 Picture
1. hdu1166 敌兵布阵
树状数组空间要求小,开个 n n n即可
#include <bits/stdc++.h>
using namespace std;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
for(int i=1;i<=t;i++){
int n, l, r;
cin >> n;
vector<int> tree(n + 1);
function<int(int)> lowbit = [&](int x){
return x & -x;
};
function<int(int)> query = [&](int x){
int ans = 0;
while(x > 0){
ans += tree[x];
x -= lowbit(x);
}
return ans;
};
function<void(int, int)> modify = [&](int x, int d){
while(x <= n){
tree[x] += d;
x += lowbit(x);
}
};
for(int j=1;j<=n;j++){
int x;
cin >> x;
modify(j, x);
}
string s;
cout << "Case " << i << ":\n";
while(cin >> s){
if(s == "End") break;
if(s == "Query"){
cin >> l >> r;
cout << query(r) - query(l - 1) << '\n';
}else if(s == "Add"){
cin >> l >> r;
modify(l, r);
}else{
cin >> l >> r;
modify(l, -r);
}
}
}
return 0;
}
线段树单点修改,区间查询
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 5e4 + 100;
struct SegmentTree{
int val;
int lazy;
}segtree[MAXN << 2];
int Data[MAXN];
void Push_Up(int Root){
segtree[Root].val = segtree[Root << 1].val + segtree[Root << 1 | 1].val;
}
void Push_Down(int Root, int m){
if(segtree[Root].lazy){
int x = segtree[Root].lazy;
segtree[Root].lazy = 0;
segtree[Root << 1].val += (m - (m >> 1)) * x;
segtree[Root << 1 | 1].val += (m >> 1) * x;
segtree[Root << 1].lazy += x;
segtree[Root << 1 | 1].lazy += x;
}
}
void Build_Tree(int Root, int L, int R){
if(L == R){
segtree[Root].val = Data[L];
segtree[Root].lazy = 0;
return;
}
int mid = (R - L >> 1) + L;
Build_Tree(Root << 1, L, mid);
Build_Tree(Root << 1 | 1, mid + 1, R);
Push_Up(Root);
}
void modify(int Root, int L, int R, int x, int d){
if(L == x && R == x){
segtree[Root].val += d;
return;
}
int mid = (R - L >> 1) + L;
Push_Down(Root, R - L + 1);
if(x <= mid) modify(Root << 1, L, mid, x, d);
if(mid < x) modify(Root << 1 | 1, mid + 1, R, x, d);
Push_Up(Root);
}
int query(int Root, int L, int R, int a, int b){
if(a <= L && R <= b){
return segtree[Root].val;
}
int mid = (R - L >> 1) + L;
int ans = 0;
Push_Down(Root, R - L + 1);
if(a <= mid) ans += query(Root << 1, L, mid, a, b);
if(mid < b) ans += query(Root << 1 | 1, mid + 1, R, a, b);
return ans;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, t, l, r;
cin >> t;
for(int j=1;j<=t;j++){
cin >> n;
for(int i=1;i<=n;i++) cin >> Data[i];
Build_Tree(1, 1, n);
string s;
cout << "Case " << j << ":\n";
while(cin >> s){
if(s == "End") break;
cin >> l >> r;
if(s == "Query"){
cout << query(1, 1, n, l, r) << '\n';
}else if(s == "Add"){
modify(1, 1, n, l, r);
}else{
modify(1, 1, n, l, -r);
}
}
}
return 0;
}
2. hdu 1754 I Hate It
区间最大值,为什么不需要lazy?因为每次查询都是要logn的递归到底,不存在延迟修改的问题
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 2e5 + 100;
struct SegmentTree{
int val;
}segtree[MAXN << 2];
int Data[MAXN];
void Push_Up(int Root){
segtree[Root].val = max(segtree[Root << 1].val, segtree[Root << 1 | 1].val);
}
void Build(int Root, int L, int R){
if(L == R){
segtree[Root].val = Data[L];
return;
}
int mid = (R - L >> 1) + L;
Build(Root << 1, L, mid);
Build(Root << 1 | 1, mid + 1, R);
Push_Up(Root);
}
int query(int Root, int L, int R, int a, int b){
if(a <= L && R <= b){
return segtree[Root].val;
}
int ans = 0;
int mid = (R - L >> 1) + L;
if(a <= mid) ans = max(ans, query(Root << 1, L, mid, a, b));
if(mid < b) ans = max(ans, query(Root << 1 | 1, mid + 1, R, a, b));
return ans;
}
void modify(int Root, int L, int R, int x, int d){
if(L == R && x == L){
segtree[Root].val = d;
return;
}
int mid = (R - L >> 1) + L;
if(x <= mid) modify(Root << 1, L, mid, x, d);
if(mid < x) modify(Root << 1 | 1, mid + 1, R, x, d);
Push_Up(Root);
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int n, m, l, r;
while(cin >> n >> m){
for(int i=1;i<=n;i++){
cin >> Data[i];
}
Build(1, 1, n);
char c;
while(m--){
cin >> c >> l >> r;
if(c == 'Q'){
cout << query(1, 1, n, l, r) << '\n';
}else{
modify(1, 1, n, l, r);
}
}
}
return 0;
}
3. poj 3468 A Simple Problem with Integers
区间修改,区间查询,注意每层都需要 P u s h _ D o w n Push\_Down Push_Down,这样才能使 l a z y lazy lazy不互相覆盖
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <vector>
#include <cmath>
#include <queue>
#include <stack>
#include <map>
#include <set>
#include <list>
#include <iomanip>
#include <climits>
using namespace std;
const int MAXN = 1e5 + 100;
typedef long long ll;
struct SegmentTree{
ll val;
ll lazy;
}segtree[MAXN << 2];
ll Data[MAXN];
void Push_Up(int Root){
segtree[Root].val = segtree[Root << 1].val + segtree[Root << 1 | 1].val;
}
void Push_Down(int Root, ll m){
if(segtree[Root].lazy){
ll x = segtree[Root].lazy;
segtree[Root].lazy = 0;
segtree[Root << 1].val += x * (m - (m >> 1));
segtree[Root << 1 | 1].val += x * (m >> 1);
segtree[Root << 1].lazy += x;
segtree[Root << 1 | 1].lazy += x;
}
}
void Build(int Root, int L, int R){
if(L == R){
segtree[Root].val = Data[L];
segtree[Root].lazy = 0;
return;
}
int mid = (R - L >> 1) + L;
Build(Root << 1, L, mid);
Build(Root << 1 | 1, mid + 1, R);
Push_Up(Root);
}
ll query(int Root, int L, int R, int a, int b){
if(a <= L && R <= b){
return segtree[Root].val;
}
ll ans = 0;
int mid = (R - L >> 1) + L;
Push_Down(Root, R - L + 1);
if(a <= mid) ans += query(Root << 1, L, mid, a, b);
if(mid < b) ans += query(Root << 1 | 1, mid + 1, R, a, b);
return ans;
}
void modify(int Root, int L, int R, int a, int b, ll k){
if(a <= L && R <= b){
segtree[Root].val += k * (R - L + 1);
segtree[Root].lazy += k;
return;
}
int mid = (R - L >> 1) + L;
Push_Down(Root, R - L + 1);
if(a <= mid) modify(Root << 1, L, mid, a, b, k);
if(mid < b) modify(Root << 1 | 1, mid + 1, R, a, b, k);
Push_Up(Root);
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int n, q, a, b, k;
cin >> n >> q;
for(int i=1;i<=n;i++) cin >> Data[i];
Build(1, 1, n);
char c;
while(q--){
cin >> c;
if(c == 'Q'){
cin >> a >> b;
cout << query(1, 1, n, a, b) << '\n';
}else{
cin >> a >> b >> k;
modify(1, 1, n, a, b, k);
}
}
return 0;
}
4. poj 2528 Mayor’s posters
题目意思很简单,给出若干组线段,后来的线段可以覆盖开头的线段,线段端点都是整数,问最后在上面能看到多少线段
- 需要注意的是端点的范围是
10000000
10000000
10000000,数很大,直接开线段树空间不够,所以需要离散化,考虑这样的几个线段:
[
1
,
4
]
,
[
2
,
6
]
,
[
5
,
8
]
[1,4],[2,6],[5,8]
[1,4],[2,6],[5,8],那么离散化之后应该是下面这样
1 , 2 , 4 , 5 , 6 , 8 → 1 , 2 , 3 , 4 , 5 , 6 1,2,4,5,6,8\rightarrow1,2,3,4,5,6 1,2,4,5,6,8→1,2,3,4,5,6
这样原来的几条线段就变成了 [ 1 , 3 ] , [ 2 , 5 ] , [ 4 , 6 ] [1,3],[2,5],[4,6] [1,3],[2,5],[4,6],覆盖关系是不变的,但是需要注意一个问题,因为离散化实际上是把距离压缩了,那么如果原来两条线段的距离是1,那么没事;但是如果原来两条线段的距离大于1,那么离散化之后距离就会是1,这样答案就错了,因为都是整数,这样的离散化会导致错误的覆盖 - 怎么解决这个问题?一个好方法是检查离散化之后的数组,如果发现某两个数之间的差大于1,那么就人为在中间添加一个数,最后再排个序,然后二分获取数的位置即可
- 查询时,自顶向下即可
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
const int MAXN = 1e6 + 100;
struct SegmentTree{
int val;
int lazy;
}segtree[MAXN << 2];
int ll[MAXN], rr[MAXN];
int Data[MAXN];
int vis[MAXN];
void Push_Down(int Root){
if(segtree[Root].lazy){
int x = segtree[Root].lazy;
segtree[Root << 1].val = segtree[Root << 1].lazy = x;
segtree[Root << 1 | 1].val = segtree[Root << 1 | 1].lazy = x;
segtree[Root].lazy = 0;
}
}
void Build(int Root, int L, int R){
if(L == R){
segtree[Root].val = 0;
segtree[Root].lazy = 0;
return;
}
int mid = (R - L >> 1) + L;
Build(Root << 1, L, mid);
Build(Root << 1 | 1, mid + 1, R);
}
void modify(int Root, int L, int R, int a, int b, int k){
if(a <= L && R <= b){
segtree[Root].lazy = segtree[Root].val = k;
return;
}
int mid = (R - L >> 1) + L;
Push_Down(Root);
if(a <= mid) modify(Root << 1, L, mid, a, b, k);
if(mid < b) modify(Root << 1 | 1, mid + 1, R, a, b, k);
}
int ans;
void query(int Root, int L, int R){
if(L == R){
if(!vis[segtree[Root].val]){
vis[segtree[Root].val] = 1;
ans += 1;
}
return;
}
int mid = (R - L >> 1) + L;
Push_Down(Root);
query(Root << 1, L, mid);
query(Root << 1 | 1, mid + 1, R);
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t;
cin >> t;
while(t--){
int n;
cin >> n;
int cnt = 0;
ans = 0;
for(int i=0;i<n;i++){
cin >> ll[i] >> rr[i];
Data[cnt++] = ll[i];
Data[cnt++] = rr[i];
}
sort(Data, Data + cnt);
int len = unique(Data, Data + cnt) - Data;
cnt = len;
for(int i=1;i<len;i++){
if(Data[i] - Data[i - 1] > 1){
Data[cnt++] = Data[i] - 1;
}
}
sort(Data, Data + cnt);
Build(1, 1, cnt);
for(int i=0;i<n;i++){
int L = lower_bound(Data, Data + cnt, ll[i]) - Data;
int R = lower_bound(Data, Data + cnt, rr[i]) - Data;
modify(1, 1, cnt, L + 1, R + 1, i + 1);
}
memset(vis, 0, sizeof vis);
vis[0] = 1;
query(1, 1, cnt);
cout << ans << '\n';
}
return 0;
}
5. hdu 1498 Just a Hook
- 线段树区间修改,区间求值,注意输出的格式,最后有个点,千万别忘了
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 1e5 + 100;
struct SegmentTree{
int val;
int lazy;
}segtree[MAXN << 2];
void Build(int Root, int L, int R){
if(L == R){
segtree[Root].val = 1;
segtree[Root].lazy = 0;
return;
}
int mid = (R - L >> 1) + L;
Build(Root << 1, L, mid);
Build(Root << 1 | 1, mid + 1, R);
}
void Push_Down(int Root){
if(segtree[Root].lazy){
int x = segtree[Root].lazy;
segtree[Root << 1].val = segtree[Root << 1].lazy = x;
segtree[Root << 1 | 1].val = segtree[Root << 1 | 1].lazy = x;
segtree[Root].lazy = 0;
}
}
void modify(int Root, int L, int R, int a, int b, int k){
if(a <= L && R <= b){
segtree[Root].val = segtree[Root].lazy = k;
return;
}
int mid = (R - L >> 1) + L;
Push_Down(Root);
if(a <= mid) modify(Root << 1, L, mid, a, b, k);
if(mid < b) modify(Root << 1 | 1, mid + 1, R, a, b, k);
}
int query(int Root, int L, int R){
if(L == R){
return segtree[Root].val;
}
Push_Down(Root);
int ans = 0;
int mid = (R - L >> 1) + L;
ans += query(Root << 1, L, mid);
ans += query(Root << 1 | 1, mid + 1, R);
return ans;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t, n, x, y, z, m;
cin >> t;
for(int _=1;_<=t;_++){
cin >> m;
cin >> n;
Build(1, 1, m);
for(int i=0;i<n;i++){
cin >> x >> y >> z;
modify(1, 1, m, x, y, z);
}
cout << "Case " << _ << ": The total value of the hook is ";
cout << query(1, 1, m) << '.' << '\n';
}
return 0;
}
6. zoj 1610 Count the Colors
- 线段树区间覆盖问题,问每种不同的颜色各有多少段
- 线段树根节点表示所对应区间是否由一种颜色覆盖,然后不停更新,同时lazy标记,最后用另外一个数组记录每个节点最终的情况,最后扫一遍8000个点,看有多少颜色相同的线段
- 注意线段覆盖的时候最好是一边开一边闭处理起来比较方便
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 8e3;
int segtree[N + 10 << 2];// segtree[i]表示操作过程中线段树上以i为根节点的区间颜色
int col[N + 10 << 2];// col[i]表示最后线段树上以i为根节点的区间颜色
int num[N + 10];// num[i]表示第i种颜色的段数
void Push_UP(int o){
if(segtree[o] != -1){
segtree[o << 1] = segtree[o << 1 | 1] = segtree[o];
segtree[o] = -1;
}
}
void Build(int o, int l, int r){
segtree[o] = -1;
if(l == r){
return;
}
int mid = (r - l >> 1) + l;
Build(o << 1, l, mid);
Build(o << 1 | 1, mid + 1, r);
}
void modify(int o, int l, int r, int ql, int qr, int k){
if(ql <= l && r <= qr){
segtree[o] = k;
return;
}
Push_UP(o);
int mid = (r - l >> 1) + l;
if(ql <= mid) modify(o << 1, l, mid, ql, qr, k);
if(mid < qr) modify(o << 1 | 1, mid + 1, r, ql, qr, k);
}
void query(int o, int l, int r){
if(l == r){
col[l] = segtree[o];
return;
}
int mid = (r - l >> 1) + l;
Push_UP(o);
query(o << 1, l, mid);
query(o << 1 | 1, mid + 1, r);
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
while(cin >> n && n){
Build(1, 1, N);
memset(num, 0, sizeof num);
for(int i=0;i<n;i++){
int l, r, c;
cin >> l >> r >> c;
if(l == r) continue;
modify(1, 1, N, l + 1, r, c);
}
query(1, 1, N);
for(int i=1;i<=N;i++){
while(i < N && col[i] == col[i + 1]) i += 1;
if(col[i] != -1) num[col[i]] += 1;
}
for(int i=0;i<=N;i++){
if(num[i] > 0){
cout << i << ' ' << num[i] << '\n';
}
}
cout << '\n';
}
return 0;
}
7. poj 3264 Balanced Lineup
- 静态区间最大值 − - −最小值,st表,线段树,莫队均可,这里用线段树吧
#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <stack>
#include <queue>
#include <iomanip>
#include <cstdio>
#include <set>
#include <map>
#include <cmath>
using namespace std;
typedef long long ll;
const int N = 5e4 + 100;
struct SegmentTree{
int MAX, MIN;
int lazy;
}segtree[N << 2];
void Push_Up(int o){
segtree[o].MAX = max(segtree[o << 1].MAX, segtree[o << 1 | 1].MAX);
segtree[o].MIN = min(segtree[o << 1].MIN, segtree[o << 1 | 1].MIN);
}
void Build(int o, int l, int r){
if(l == r){
cin >> segtree[o].MAX;
segtree[o].MIN = segtree[o].MAX;
segtree[o].lazy = 0;
return;
}
int mid = (r - l >> 1) + l;
Build(o << 1, l, mid);
Build(o << 1 | 1, mid + 1, r);
Push_Up(o);
}
int querymax(int o, int l, int r, int ql, int qr){
if(ql <= l && r <= qr){
return segtree[o].MAX;
}
int mid = (r - l >> 1) + l;
int ans = 0;
if(ql <= mid) ans = max(ans, querymax(o << 1, l, mid, ql, qr));
if(mid < qr) ans = max(ans, querymax(o << 1 | 1, mid + 1, r, ql, qr));
return ans;
}
int querymin(int o, int l, int r, int ql, int qr){
if(ql <= l && r <= qr){
return segtree[o].MIN;
}
int ans = 2147483647;
int mid = (r - l >> 1) + l;
if(ql <= mid) ans = min(ans, querymin(o << 1, l, mid, ql, qr));
if(mid < qr) ans = min(ans, querymin(o << 1 | 1, mid + 1, r, ql, qr));
return ans;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, q;
cin >> n >> q;
Build(1, 1, n);
while(q--){
int l, r;
cin >> l >> r;
cout << querymax(1, 1, n, l, r) - querymin(1, 1, n, l, r) << '\n';
}
return 0;
}
8. hdu 4027 Can you answer these queries?
- 每次操作把 [ l , r ] [l,r] [l,r]之间所有元素开方,查询区间和
- 因为开方的次数会很少,所以线段树维护一个区间0或1的个数和,如果这个和为区间元素个数,那么说明这个区间已经完全开方,然后求区间和即可
- 注意这个输入的 [ l , r ] [l,r] [l,r]可能是反的
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e5 + 100;
struct SegmentTree{
ll sum;// 区间和
ll tot;// 区间0或1的数量
}segtree[N << 2];
void Push_Down(int o){
segtree[o].sum = segtree[o << 1].sum + segtree[o << 1 | 1].sum;
segtree[o].tot = segtree[o << 1].tot + segtree[o << 1 | 1].tot;
}
void Build(int o, int l, int r){
if(l == r){
cin >> segtree[o].sum;
if(segtree[o].sum == 0 || segtree[o].sum == 1){
segtree[o].tot = 1;
}else{
segtree[o].tot = 0;
}
return;
}
int mid = (r - l >> 1) + l;
Build(o << 1, l, mid);
Build(o << 1 | 1, mid + 1, r);
Push_Down(o);
}
void modify(int o, int l, int r, int ml, int mr){
if(segtree[o].tot == r - l + 1){
return;
}
if(l == r){
segtree[o].sum = sqrt(segtree[o].sum);
if(segtree[o].sum == 1) segtree[o].tot = 1;
return;
}
int mid = (r - l >> 1) + l;
if(ml <= mid){
modify(o << 1, l, mid, ml, mr);
}
if(mid < mr){
modify(o << 1 | 1, mid + 1, r, ml, mr);
}
Push_Down(o);
}
ll query(int o, int l, int r, int ql, int qr){
if(ql <= l && r <= qr) return segtree[o].sum;
ll sum = 0;
int mid = (r - l >> 1) + l;
if(ql <= mid){
sum += query(o << 1, l, mid, ql, qr);
}
if(mid < qr){
sum += query(o << 1 | 1, mid + 1, r, ql, qr);
}
return sum;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
int t = 0;
while(cin >> n){
t += 1;
Build(1, 1, n);
int m;
cin >> m;
cout << "Case #" << t << ":\n";
for(int i=0;i<m;i++){
int opt, l, r;
cin >> opt >> l >> r;
if(l > r) swap(l, r);
if(opt == 0){
modify(1, 1, n, l, r);
}else{
cout << query(1, 1, n, l, r) << '\n';
}
}
cout << '\n';
}
return 0;
}
9. hdu 1540 Tunnel Warfare
- 每次D操作相当于切断两条线段,R操作复原上次操作,Q操作查询,考虑如何维护每种操作
- 此题是线段树维护连续区间的模板题
- 如何用线段树维护呢?或者说线段树应该存储什么信息,此类问题有固定套路,维护 r s u m rsum rsum和 l s u m lsum lsum,分别表示这个线段树节点表示的线段从最做往右最多多长和从右到左最多多长,那么我们每次维护这两个值,具体见代码注释
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e4 + 100;
struct SegmentTree{
int lsum;
int rsum;
}segtree[N << 2];
void Push_Down(int o, int m){
segtree[o].lsum = segtree[o << 1].lsum;
segtree[o].rsum = segtree[o << 1 | 1].rsum;
// 右子树线段的右连续子段等于区间长度, 那么根节点的右连续子段加上左连续子段的区间长度
if(segtree[o << 1 | 1].rsum == (m >> 1)) segtree[o].rsum += segtree[o << 1].rsum;
// 同理
if(segtree[o << 1].lsum == m - (m >> 1)) segtree[o].lsum += segtree[o << 1 | 1].lsum;
}
void Build(int o, int l, int r){
if(l == r){
segtree[o].lsum = segtree[o].rsum = 1;
return;
}
int mid = (r - l >> 1) + l;
Build(o << 1, l, mid);
Build(o << 1 | 1, mid + 1, r);
Push_Down(o, r - l + 1);
}
void modify(int o, int l, int r, int k, int opt){
if(l == r){
segtree[o].lsum = segtree[o].rsum = opt;
return;
}
int mid = (r - l >> 1) + l;
if(k <= mid){
modify(o << 1, l, mid, k, opt);
}else{
modify(o << 1 | 1, mid + 1, r, k, opt);
}
Push_Down(o, r - l + 1);
}
int query(int o, int l, int r, int k){
if(l == r){
return segtree[o].lsum;
}
int mid = (r - l >> 1) + l;
if(k >= mid - segtree[o << 1].rsum + 1 && k <= mid + segtree[o << 1 | 1].lsum){
// 如果在中间的连续段内部
return segtree[o << 1].rsum + segtree[o << 1 | 1].lsum;
}
if(k <= mid){
return query(o << 1, l, mid, k);
}else{
return query(o << 1 | 1, mid + 1, r, k);
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n, m;
while(cin >> n >> m){
stack<int> st;
Build(1, 1, n);
for(int i=0;i<m;i++){
char c;
cin >> c;
if(c == 'D'){
int u;
cin >> u;
st.push(u);
modify(1, 1, n, u, 0);
}else if(c == 'Q'){
int u;
cin >> u;
cout << query(1, 1, n, u) << '\n';
}else{
assert(!st.empty());
int v = st.top();
st.pop();
modify(1, 1, n, v, 1);
}
}
}
return 0;
}
10. hdu 3974 Assign the task
- 按照给定条件建树,然后根据树链剖分的思想,统计出每个节点的 d f n dfn dfn,然后用线段树维护子树,因为子树内部的节点编号是连续的,区间修改和单点查询即可
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e4 + 100;
struct SegmentTree{
int val;
int tag;
}segtree[N << 2];
void Push_Up(int o){
if(segtree[o].tag){
segtree[o << 1].tag = 1;
segtree[o << 1 | 1].tag = 1;
segtree[o << 1].val = segtree[o].val;
segtree[o << 1 | 1].val = segtree[o].val;
}
segtree[o].tag = 0;
}
void Build(int o, int l, int r){
segtree[o].val = -1;
segtree[o].tag = 0;
if(l == r){
return;
}
int mid = (r - l >> 1) + l;
Build(o << 1, l, mid);
Build(o << 1 | 1, mid + 1, r);
}
void modify(int o, int l, int r, int ml, int mr, int k){
if(r < ml || l > mr) return;
int mid = (r - l >> 1) + l;
if(ml <= l && r <= mr){
segtree[o].val = k;
segtree[o].tag = 1;
return;
}
Push_Up(o);
if(ml <= mid){
modify(o << 1, l, mid, ml, mr, k);
}
if(mid < mr){
modify(o << 1 | 1, mid + 1, r, ml, mr, k);
}
}
int query(int o, int l, int r, int k){
if(l == r){
return segtree[o].val;
}
int mid = (r - l >> 1) + l;
Push_Up(o);
if(k <= mid){
return query(o << 1, l, mid, k);
}else{
return query(o << 1 | 1, mid + 1, r, k);
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
for(int kase=1;kase<=t;kase++){
int n;
cin >> n;
vector<vector<int> > g(n + 1);
vector<int> sz(n + 1), degree(n + 1), id(n + 1);
for(int i=1;i<n;i++){
int u, v;
cin >> u >> v;
g[v].push_back(u);
degree[u] += 1;
}
int dfn = 0;
function<void(int)> Dfs = [&](int u){
sz[u] = 1;
id[u] = ++dfn;
for(auto v : g[u]){
Dfs(v);
sz[u] += sz[v];
}
};
for(int i=1;i<=n;i++){
if(degree[i] == 0){
Dfs(i);
}
}
int m;
cin >> m;
Build(1, 1, n);
cout << "Case #" << kase << ":\n";
while(m--){
char c;
cin >> c;
if(c == 'C'){
int u;
cin >> u;
cout << query(1, 1, n, id[u]) << '\n';
}else{
int u, v;
cin >> u >> v;
modify(1, 1, n, id[u], id[u] + sz[u] - 1, v);
}
}
}
return 0;
}
12. hdu 4614 Vases and Flowers
- 两种操作,第一种操作是收到 f f f朵花,并放在 [ a , n ] [a,n] [a,n]内的空花瓶中,每个空花瓶放一朵,如果不空则跳过该花瓶;第二种操作是清空 [ a , b ] [a,b] [a,b]内所有花瓶,并输出被丢弃的花朵数量
- 线段树维护两个值,一个是区间空花瓶数,另一个是 l a z y lazy lazy
- 对于操作2,我们只需要作差即可,然后更新花瓶
- 对于操作1,我们需要知道第一个空花瓶和能够放置的最后一个空花瓶,同一起点的区间花瓶数量显然具有单调性,所以可以进行二分,操作之后再次更新花瓶数量
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 5e5 + 10;
struct SegmentTree{
int sum;// [l, r]的空花瓶数量
int lazy;// 清空还是没清空
}segtree[N << 2];
void Push_Up(int o){
segtree[o].sum = segtree[o << 1].sum + segtree[o << 1 | 1].sum;
}
void Push_Down(int o, int l, int r){
if(segtree[o].lazy != -1){
segtree[o << 1].lazy = segtree[o << 1 | 1].lazy = segtree[o].lazy;
int c = segtree[o].lazy;
segtree[o].lazy = -1;
int mid = (r - l >> 1) + l;
segtree[o << 1].sum = (mid - l + 1) * c;
segtree[o << 1 | 1].sum = (r - mid) * c;
}
}
void Build(int o, int l, int r){
segtree[o].sum = r - l + 1;
segtree[o].lazy = -1;
if(l == r){
return;
}
int mid = (r - l >> 1) + l;
Build(o << 1, l, mid);
Build(o << 1 | 1, mid + 1, r);
}
void modify(int o, int l, int r, int ql, int qr, int c){
Push_Down(o, l, r);
if(l > qr || r < ql) return;
if(ql <= l && r <= qr){
segtree[o].lazy = c;
segtree[o].sum = (r - l + 1) * c;
return;
}
int mid = (r - l >> 1) + l;
if(ql <= mid){
modify(o << 1, l, mid, ql, qr, c);
}
if(mid < qr){
modify(o << 1 | 1, mid + 1, r, ql, qr, c);
}
Push_Up(o);
}
int query(int o, int l, int r, int ql, int qr){
if(l > qr || r < ql) return 0;
if(ql <= l && r <= qr){
return segtree[o].sum;
}
Push_Down(o, l, r);
int mid = (r - l >> 1) + l;
int ans = 0;
if(ql <= mid){
ans += query(o << 1, l, mid, ql, qr);
}
if(mid < qr){
ans += query(o << 1 | 1, mid + 1, r, ql, qr);
}
return ans;
}
int Get(int n, int now, int p){
int l = now;
int r = n;
while(l <= r){
int mid = (r - l >> 1) + l;
if(query(1, 1, n, now, mid) >= p){
r = mid - 1;
}else{
l = mid + 1;
}
}
return r + 1;
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int t;
cin >> t;
while(t--){
int n, m;
cin >> n >> m;
Build(1, 1, n);
for(int i=0;i<m;i++){
int k, a, f;
cin >> k >> a >> f;
if(k == 1){
a += 1;
int num = query(1, 1, n, a, n);
if(num == 0){
cout << "Can not put any one.\n";
}else{
int l = Get(n, a, 1);
int r = Get(n, a, min(f, num));
cout << l - 1 << ' ' << r - 1 << '\n';
modify(1, 1, n, l, r, 0);
}
}else{
a += 1;
f += 1;
cout << f - a + 1 - query(1, 1, n, a, f) << '\n';
modify(1, 1, n, a, f, 1);
}
}
cout << '\n';
}
return 0;
}
14. poj 1177 Picture
- 扫描线求矩形周长和,长度很小
- 先把每条线段按照高度从小到大排序,高度相同就按照下上排序,然后从下往上依次向线段树加线段
- 线段树每个节点记录的信息有这个节点所代表的线段内部被覆盖的长度是多少、这个线段被完全覆盖的次数、线段的做右端点是否被覆盖、线段被几条互不相交的线段覆盖(用来统计有多少竖边)
- 然后在加点的过程中,因为根节点表示的是 [ 1 , m x ] [1,mx] [1,mx]线段的覆盖情况,所以这时候竖边的长度就是2*整个线段有多少不相交的线段分隔,横边的长度应该等于当前横边的覆盖长度减去上一次横边的覆盖长度,取绝对值,因为要把横边的多余部分加上
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
#include <map>
#include <iomanip>
using namespace std;
typedef long long ll;
const int N = 1e5 + 100;
struct Seg{
int L, R;
int height;
int flag;
}seg[N];
int tot = 0;
void Add_Seg(int l, int r, int h, int f){
seg[tot].L = l;
seg[tot].R = r;
seg[tot].height = h;
seg[tot].flag = f;
tot += 1;
}
struct SegmentTree{
int num;// 区间由多少不相交的线段分隔
int sum;// 区间被完整覆盖的次数
int len;// 区间被覆盖的长度
int lflag;// 左端点是否被覆盖
int rflag;// 右端点是否被覆盖
}tree[N << 2];
void Push_Up(int o, int l, int r){
if(tree[o].sum){// 完整覆盖
tree[o].num = 1;
tree[o].len = r - l + 1;
tree[o].lflag = tree[o].rflag = 1;
}else if(l == r){// 叶子节点
tree[o].len = tree[o].num = tree[o].lflag = tree[o].rflag = 0;
}else{
tree[o].num = tree[o << 1].num + tree[o << 1 | 1].num;
tree[o].len = tree[o << 1].len + tree[o << 1 | 1].len;
if(tree[o << 1].rflag && tree[o << 1 | 1].lflag) tree[o].num -= 1;
tree[o].lflag = tree[o << 1].lflag;
tree[o].rflag = tree[o << 1 | 1].rflag;
}
}
void modify(int o, int l, int r, int M_l, int M_r, int k){
if(M_l <= l && r <= M_r){
tree[o].sum += k;
Push_Up(o, l, r);
return;
}
int mid = (r - l >> 1) + l;
if(M_l <= mid) modify(o << 1, l, mid, M_l, M_r, k);
if(mid < M_r) modify(o << 1 | 1, mid + 1, r, M_l, M_r, k);
Push_Up(o, l, r);
}
bool cmp(Seg x, Seg y){
return x.height < y.height || (x.height == y.height && x.flag > y.flag);
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cout.tie(0);
int n;
cin >> n;
int mx = -2e9;
int mn = 2e9;
for(int i=0;i<n;i++){
int x1, x2, y1, y2;
cin >> x1 >> y1 >> x2 >> y2;
mx = max(mx, max(x1, x2));
mn = min(mn, min(x1, x2));
Add_Seg(x1, x2, y1, 1);
Add_Seg(x1, x2, y2, -1);
}
if(mn <= 0){
for(int i=0;i<tot;i++){
seg[i].L -= mn - 1;
seg[i].R -= mn - 1;
}
mx -= mn - 1;
}
sort(seg, seg + tot, cmp);
ll ans = 0;
int last = 0;
for(int i=0;i<tot;i++){
modify(1, 1, mx, seg[i].L, seg[i].R - 1, seg[i].flag);// 右端点-1的原因是
// 线段树是闭区间的,为了保证求得的线段长度保持在r - l,需要减少一个单位的偏移
while(i < tot - 1 && seg[i].height == seg[i + 1].height && seg[i].flag == seg[i + 1].flag){
i += 1;
modify(1, 1, mx, seg[i].L, seg[i].R - 1, seg[i].flag);
}
ans += abs(tree[1].len - last);
last = tree[1].len;
ans += 2ll * tree[1].num * (seg[i + 1].height - seg[i].height);
}
cout << ans;
return 0;
}