poj线段树
2013-11-18 16:02
225 查看
解题报告
做了两个poj线段树的题目,感觉不错。
线段树是一个进行区间操作的好工具!我们正常使用的大多是二叉树,也就是将一个区间二分分解,将我们想要记录的区间的信息放入二叉树节点中去,然后进行查找和修改都是logn的时间,由于是二分分解的,于是他是一颗标准的类堆状树,即,他的深度很低,很饱满,效率很好。
因为线段树是进行区间操作的,因此看到和区间有关系的问题时,先想一想一块区间的某些信息能否与题目要解决的问题合在一起。这里是使用线段是最难的地方吧。一定想清楚了。
//poj3321
很普通的区间求和的题目,但是先得将题目中的多叉树先根或后根遍历,得到序列,并记录节点在树中位置,和节点儿子的个数(化为序列后,u的儿子个数就是从u开始求和的区间范围)。
#include
<cstdio>
#include
<cstring>
#define
MAXN 100100
#define
root 1
struct
Segment_Tree{
int son_l, son_r;
int led, red;
int sum;
int flag;
};
Segment_Tree S_T[MAXN * 10];
struct
EDGE{
int to, next;
}
edges[MAXN * 2];
int
head[MAXN], ad, son[MAXN], site[MAXN], vis[MAXN];
int n,
Time, ANS;
void
clear(){
memset(head, -1, sizeof(head));
ad = 0;
}
void
insert(int u, int v){
edges[ad].to = v; edges[ad].next = head[u]; head[u] = ad
++;
}
void
dfs(int u){
vis[u] = 1;
site[u] = ++ Time;
son[u] = 0;
for(int p = head[u]; ~p; p = edges[p].next){
int v = edges[p].to;
if(!vis[v]){
dfs(v);
son[u] += son[v] + 1;
}
}
}
void
Built_Segment_Tree(int rt, int l, int r){
S_T[rt].led = l;
S_T[rt].red = r;
S_T[rt].flag = 1;
if(l == r) S_T[rt].sum = 1;
else S_T[rt].sum = 0;
if(l != r){
Built_Segment_Tree((S_T[rt].son_l = rt * 2), l, (l + r) /
2);
S_T[rt].sum += S_T[S_T[rt].son_l].sum;
Built_Segment_Tree((S_T[rt].son_r = rt * 2 + 1), (l + r) / 2 + 1,
r);
S_T[rt].sum += S_T[S_T[rt].son_r].sum;
}
else S_T[rt].son_l = S_T[rt].son_r = -1;
}
void
Change(int l, int r, int x, int rt){
if(l == r && l == x){
if(S_T[rt].flag){
S_T[rt].flag = 0;
S_T[rt].sum -= 1;
}
else{
S_T[rt].flag = 1;
S_T[rt].sum += 1;
}
}
else{
int mid = (l + r) / 2;
if(x > mid){
Change(mid + 1, r, x, S_T[rt].son_r);
if(S_T[S_T[rt].son_r].flag){
S_T[rt].flag = 1;
S_T[rt].sum += 1;
}
else{
S_T[rt].flag = 0;
S_T[rt].sum -= 1;
}
}
else{
Change(l, mid, x, S_T[rt].son_l);
if(S_T[S_T[rt].son_l].flag){
S_T[rt].flag = 1;
S_T[rt].sum += 1;
}
else{
S_T[rt].flag = 0;
S_T[rt].sum -= 1;
}
}
}
}
void
answer(int rt, int pl, int pr){
if(S_T[rt].led >= pl
&& S_T[rt].red <=
pr){ ANS += S_T[rt].sum; return;}
if(S_T[rt].led > pr || S_T[rt].red <
pl) return;
answer(S_T[rt].son_l, pl, pr);
answer(S_T[rt].son_r, pl, pr);
}
int
main(){
int n, i, x, y, m;
char ch[2];
while(~scanf("%d", &n)){
clear();
for(i = 1; i < n; ++ i){
scanf("%d %d", &x, &y);
insert(x, y);
insert(y, x);
}
memset(vis, 0, sizeof(vis));
Time = 0;
dfs(root);
Built_Segment_Tree(root, 1, n);
scanf("%d", &m);
while(m --){
scanf("%s %d", ch, &x);
switch(ch[0]){
case 'Q' : ANS = 0;
answer(root, site[x], site[x] + son[x]);
printf("%d\n", ANS);
break;
case 'C' : Change(1, n, site[x], root);
break;
}
}
}
return 0;
}
//poj2528
区间染色问题。我们的区间记录的信息是这段区间内每个点的颜色,也就是说只有区间内颜色有一样的区间才是我们要用的有价值的。要注意的是:假设之前有一区间[2,5]颜色都是1,现在将[3,5]的颜色染成2,那么我们依然需要再次将[2,2]的颜色染成1,因为这个操作破坏了[2,5]区间的特征,[2,5]已经不能代表它里面的所有点的信息,我们需要给每个点再找一个“区间代表”。
#include <cstdio>
#include <map>
#include <algorithm>
using namespace std;
#define MAXN (10100 * 2)
#define root 1
struct Segment_Tree_node{
int led, red;
int son_l, son_r;
int flag;
int colour;
}S_T[MAXN * 10];
struct Comp{
bool operator()(constint
&A, const
int &B)const{
return A > B;
}
};
map<int,int,
Comp> M;
struct LINE_node{
int l, r;
} edges[MAXN];
int num[MAXN], n;
void Disperse(int
m){
sort(num, num + m);
M[num[0]] = 1;
for(int i = 1, t =
1;i < m; ++ i)
if(num[i]!= num[i - 1]){
if(num[i] - num[i - 1] >
1)
M[num[i]] = M[num[i- 1]] + 2;
else
M[num[i]] = M[num[i- 1]] + 1;
}
for(int i = 0; i
<n; i ++){
edges[i].l = M[edges[i].l];
edges[i].r = M[edges[i].r];
}
}
void Bulit_Segment_Tree(int rt, int l,
intr){
S_T[rt].led = l;
S_T[rt].red = r;
S_T[rt].flag = 0;
if(l !=r){
Bulit_Segment_Tree(S_T[rt].son_l =rt * 2, l, (l +
r) / 2);
Bulit_Segment_Tree(S_T[rt].son_r =rt * 2 + 1, (l +
r) /2 + 1, r);
}
else S_T[rt].son_l =S_T[rt].son_r = -1;
}
void Change(int rt,
int pl, intpr, int
col){
if(pr <pl) return;
if(S_T[rt].led>= pl
&& S_T[rt].red <=
pr){
S_T[rt].colour = col;
S_T[rt].flag = 1;
return;
}
if(S_T[rt].led> pr || S_T[rt].red
< pl) return;
if(S_T[rt].led<= pl
&& S_T[rt].red >=
pr){
if(S_T[rt].flag){
S_T[rt].flag = 0;
Change(S_T[rt].son_l, pl,pr, col);
Change(S_T[rt].son_r, pl,pr, col);
Change(rt, S_T[rt].led,pl - 1,
S_T[rt].colour);
Change(rt, pr +1, S_T[rt].red,
S_T[rt].colour);
}
else{
Change(S_T[rt].son_l, pl,pr, col);
Change(S_T[rt].son_r, pl,pr, col);
}
}
else{
if(S_T[rt].led>= pl){
if(S_T[rt].flag){
S_T[rt].flag = 0;
Change(S_T[rt].son_l, pl,pr, col);
Change(S_T[rt].son_r, pl,pr, col);
Change(rt, pr +1, S_T[rt].red,
S_T[rt].colour);
}
else{
Change(S_T[rt].son_l, pl,pr, col);
Change(S_T[rt].son_r, pl,pr, col);
}
}
else{
if(S_T[rt].flag){
S_T[rt].flag = 0;
Change(S_T[rt].son_l, pl,pr, col);
Change(S_T[rt].son_r, pl,pr, col);
Change(rt, S_T[rt].led,pl - 1,
S_T[rt].colour);
}
else{
Change(S_T[rt].son_l, pl,pr, col);
Change(S_T[rt].son_r, pl,pr, col);
}
}
}
}
int Get_colour(int rt,
int x){
if(S_T[rt].led== S_T[rt].red)return S_T[rt].flag? S_T[rt].colour :
-1;
if(S_T[rt].flag)return S_T[rt].colour;
if(x >(S_T[rt].led + S_T[rt].red) /
2) return Get_colour(S_T[rt].son_r,
x);
else Get_colour(S_T[rt].son_l, x);
}
int main(){
int i, CASE, m, col;
scanf("%d",&CASE);
while(CASE--){
scanf("%d",&n);
m = 0;
for(i =0; i < n; ++ i){
scanf("%d %d",
&edges[i].l,
&edges[i].r);
num[m ++] = edges[i].l;
num[m ++] = edges[i].r;
}
Disperse(m);
m = M[num[m- 1]];
Bulit_Segment_Tree(root, 1, m);
for(i =0; i < n; ++ i)
Change(root, edges[i].l,edges[i].r, i +
1);
memset(num, 0, sizeof(num));
int ans = 0;
for(i =1; i <= m; i ++){
col = Get_colour(root,i);
if(col == -1) continue;
if(!num[col]){
num[col] = 1;
ans ++;
}
}
printf("%d\n",ans);
}
return 0;
}
做了两个poj线段树的题目,感觉不错。
线段树是一个进行区间操作的好工具!我们正常使用的大多是二叉树,也就是将一个区间二分分解,将我们想要记录的区间的信息放入二叉树节点中去,然后进行查找和修改都是logn的时间,由于是二分分解的,于是他是一颗标准的类堆状树,即,他的深度很低,很饱满,效率很好。
因为线段树是进行区间操作的,因此看到和区间有关系的问题时,先想一想一块区间的某些信息能否与题目要解决的问题合在一起。这里是使用线段是最难的地方吧。一定想清楚了。
//poj3321
很普通的区间求和的题目,但是先得将题目中的多叉树先根或后根遍历,得到序列,并记录节点在树中位置,和节点儿子的个数(化为序列后,u的儿子个数就是从u开始求和的区间范围)。
#include
<cstdio>
#include
<cstring>
#define
MAXN 100100
#define
root 1
struct
Segment_Tree{
int son_l, son_r;
int led, red;
int sum;
int flag;
};
Segment_Tree S_T[MAXN * 10];
struct
EDGE{
int to, next;
}
edges[MAXN * 2];
int
head[MAXN], ad, son[MAXN], site[MAXN], vis[MAXN];
int n,
Time, ANS;
void
clear(){
memset(head, -1, sizeof(head));
ad = 0;
}
void
insert(int u, int v){
edges[ad].to = v; edges[ad].next = head[u]; head[u] = ad
++;
}
void
dfs(int u){
vis[u] = 1;
site[u] = ++ Time;
son[u] = 0;
for(int p = head[u]; ~p; p = edges[p].next){
int v = edges[p].to;
if(!vis[v]){
dfs(v);
son[u] += son[v] + 1;
}
}
}
void
Built_Segment_Tree(int rt, int l, int r){
S_T[rt].led = l;
S_T[rt].red = r;
S_T[rt].flag = 1;
if(l == r) S_T[rt].sum = 1;
else S_T[rt].sum = 0;
if(l != r){
Built_Segment_Tree((S_T[rt].son_l = rt * 2), l, (l + r) /
2);
S_T[rt].sum += S_T[S_T[rt].son_l].sum;
Built_Segment_Tree((S_T[rt].son_r = rt * 2 + 1), (l + r) / 2 + 1,
r);
S_T[rt].sum += S_T[S_T[rt].son_r].sum;
}
else S_T[rt].son_l = S_T[rt].son_r = -1;
}
void
Change(int l, int r, int x, int rt){
if(l == r && l == x){
if(S_T[rt].flag){
S_T[rt].flag = 0;
S_T[rt].sum -= 1;
}
else{
S_T[rt].flag = 1;
S_T[rt].sum += 1;
}
}
else{
int mid = (l + r) / 2;
if(x > mid){
Change(mid + 1, r, x, S_T[rt].son_r);
if(S_T[S_T[rt].son_r].flag){
S_T[rt].flag = 1;
S_T[rt].sum += 1;
}
else{
S_T[rt].flag = 0;
S_T[rt].sum -= 1;
}
}
else{
Change(l, mid, x, S_T[rt].son_l);
if(S_T[S_T[rt].son_l].flag){
S_T[rt].flag = 1;
S_T[rt].sum += 1;
}
else{
S_T[rt].flag = 0;
S_T[rt].sum -= 1;
}
}
}
}
void
answer(int rt, int pl, int pr){
if(S_T[rt].led >= pl
&& S_T[rt].red <=
pr){ ANS += S_T[rt].sum; return;}
if(S_T[rt].led > pr || S_T[rt].red <
pl) return;
answer(S_T[rt].son_l, pl, pr);
answer(S_T[rt].son_r, pl, pr);
}
int
main(){
int n, i, x, y, m;
char ch[2];
while(~scanf("%d", &n)){
clear();
for(i = 1; i < n; ++ i){
scanf("%d %d", &x, &y);
insert(x, y);
insert(y, x);
}
memset(vis, 0, sizeof(vis));
Time = 0;
dfs(root);
Built_Segment_Tree(root, 1, n);
scanf("%d", &m);
while(m --){
scanf("%s %d", ch, &x);
switch(ch[0]){
case 'Q' : ANS = 0;
answer(root, site[x], site[x] + son[x]);
printf("%d\n", ANS);
break;
case 'C' : Change(1, n, site[x], root);
break;
}
}
}
return 0;
}
//poj2528
区间染色问题。我们的区间记录的信息是这段区间内每个点的颜色,也就是说只有区间内颜色有一样的区间才是我们要用的有价值的。要注意的是:假设之前有一区间[2,5]颜色都是1,现在将[3,5]的颜色染成2,那么我们依然需要再次将[2,2]的颜色染成1,因为这个操作破坏了[2,5]区间的特征,[2,5]已经不能代表它里面的所有点的信息,我们需要给每个点再找一个“区间代表”。
#include <cstdio>
#include <map>
#include <algorithm>
using namespace std;
#define MAXN (10100 * 2)
#define root 1
struct Segment_Tree_node{
int led, red;
int son_l, son_r;
int flag;
int colour;
}S_T[MAXN * 10];
struct Comp{
bool operator()(constint
&A, const
int &B)const{
return A > B;
}
};
map<int,int,
Comp> M;
struct LINE_node{
int l, r;
} edges[MAXN];
int num[MAXN], n;
void Disperse(int
m){
sort(num, num + m);
M[num[0]] = 1;
for(int i = 1, t =
1;i < m; ++ i)
if(num[i]!= num[i - 1]){
if(num[i] - num[i - 1] >
1)
M[num[i]] = M[num[i- 1]] + 2;
else
M[num[i]] = M[num[i- 1]] + 1;
}
for(int i = 0; i
<n; i ++){
edges[i].l = M[edges[i].l];
edges[i].r = M[edges[i].r];
}
}
void Bulit_Segment_Tree(int rt, int l,
intr){
S_T[rt].led = l;
S_T[rt].red = r;
S_T[rt].flag = 0;
if(l !=r){
Bulit_Segment_Tree(S_T[rt].son_l =rt * 2, l, (l +
r) / 2);
Bulit_Segment_Tree(S_T[rt].son_r =rt * 2 + 1, (l +
r) /2 + 1, r);
}
else S_T[rt].son_l =S_T[rt].son_r = -1;
}
void Change(int rt,
int pl, intpr, int
col){
if(pr <pl) return;
if(S_T[rt].led>= pl
&& S_T[rt].red <=
pr){
S_T[rt].colour = col;
S_T[rt].flag = 1;
return;
}
if(S_T[rt].led> pr || S_T[rt].red
< pl) return;
if(S_T[rt].led<= pl
&& S_T[rt].red >=
pr){
if(S_T[rt].flag){
S_T[rt].flag = 0;
Change(S_T[rt].son_l, pl,pr, col);
Change(S_T[rt].son_r, pl,pr, col);
Change(rt, S_T[rt].led,pl - 1,
S_T[rt].colour);
Change(rt, pr +1, S_T[rt].red,
S_T[rt].colour);
}
else{
Change(S_T[rt].son_l, pl,pr, col);
Change(S_T[rt].son_r, pl,pr, col);
}
}
else{
if(S_T[rt].led>= pl){
if(S_T[rt].flag){
S_T[rt].flag = 0;
Change(S_T[rt].son_l, pl,pr, col);
Change(S_T[rt].son_r, pl,pr, col);
Change(rt, pr +1, S_T[rt].red,
S_T[rt].colour);
}
else{
Change(S_T[rt].son_l, pl,pr, col);
Change(S_T[rt].son_r, pl,pr, col);
}
}
else{
if(S_T[rt].flag){
S_T[rt].flag = 0;
Change(S_T[rt].son_l, pl,pr, col);
Change(S_T[rt].son_r, pl,pr, col);
Change(rt, S_T[rt].led,pl - 1,
S_T[rt].colour);
}
else{
Change(S_T[rt].son_l, pl,pr, col);
Change(S_T[rt].son_r, pl,pr, col);
}
}
}
}
int Get_colour(int rt,
int x){
if(S_T[rt].led== S_T[rt].red)return S_T[rt].flag? S_T[rt].colour :
-1;
if(S_T[rt].flag)return S_T[rt].colour;
if(x >(S_T[rt].led + S_T[rt].red) /
2) return Get_colour(S_T[rt].son_r,
x);
else Get_colour(S_T[rt].son_l, x);
}
int main(){
int i, CASE, m, col;
scanf("%d",&CASE);
while(CASE--){
scanf("%d",&n);
m = 0;
for(i =0; i < n; ++ i){
scanf("%d %d",
&edges[i].l,
&edges[i].r);
num[m ++] = edges[i].l;
num[m ++] = edges[i].r;
}
Disperse(m);
m = M[num[m- 1]];
Bulit_Segment_Tree(root, 1, m);
for(i =0; i < n; ++ i)
Change(root, edges[i].l,edges[i].r, i +
1);
memset(num, 0, sizeof(num));
int ans = 0;
for(i =1; i <= m; i ++){
col = Get_colour(root,i);
if(col == -1) continue;
if(!num[col]){
num[col] = 1;
ans ++;
}
}
printf("%d\n",ans);
}
return 0;
}
相关文章推荐
- 多校联合赛&nbsp;&nbsp;&nbsp;第一场
- poj&nbsp;2447&nbsp;代码改正
- poj1151&nbsp;矩形面积并
- poj1149&nbsp;最大流
- poj2679&nbsp;&nbsp;最短路+受负环影响点的判…
- 用户增加,功能扩展时系统如何升级 架构改变
- Android开发之--Auto Complete TextView的使用
- Excel中设置下拉列表的来源怎么选择其他工作表的内容
- 需要整理的
- 6个常见的 PHP 安全性攻击
- Data Domain和Avamar到底有什么不同呢?
- html_day3---表单元素
- JAVA 学习笔记一
- dedecms 全局标签 使用
- poj1180&nbsp;dp斜率优化
- hdu3507&nbsp;动态规划+斜率优化
- hdu2971&nbsp;递推+矩阵快速幂
- poj1042&nbsp;动态规划
- hdu3335&nbsp;有向图最少路径覆盖
- poj2942&nbsp;点双连通分量