POJ - 3237 Tree (树链剖分+线段树+区间修改)
2017-10-17 15:06
351 查看
Tree
Description
You are given a tree with N nodes. The tree’s nodes are numbered 1 through N and its edges are numbered 1 through N − 1. Each edge is associated with a weight. Then you are to execute a series of instructions on the tree. The instructions
can be one of the following forms:
Input
The input contains multiple test cases. The first line of input contains an integer t (t ≤ 20), the number of test cases. Then follow the test cases.
Each test case is preceded by an empty line. The first nonempty line of its contains N (N ≤ 10,000). The next N − 1 lines each contains three integers a, b and c, describing an edge connecting nodes a and b with
weight c. The edges are numbered in the order they appear in the input. Below them are the instructions, each sticking to the specification above. A lines with the word “
Output
For each “
Sample Input
Sample Output
题意:给你一个树和三种操作,分别为求最大权值,单点更新,区间更新
解题思路:negate是取反的意思,所以要记录最大值和最小值……其他就套模板了。
#include<iostream>
#include<deque>
#include<memory.h>
#include<stdio.h>
#include<map>
#include<string>
#include<algorithm>
#include<vector>
#include<math.h>
#include<stack>
#include<queue>
#include<bitset>
#include<set>
#define INF (0x3f3f3f3f)
using namespace std;
typedef long long int ll;
const int MAXN=100010;
//邻接图部分
struct Edge{
int to;
int next;
}e[MAXN*2];
int head[MAXN],edge_num;
void insert_edge(int u,int v){
e[edge_num].to=v;
e[edge_num].next=head[u];
head[u]=edge_num++;
}
//树链剖分部分
int top[MAXN];//重链顶点
int fa[MAXN];//父亲节点
int deep[MAXN];//深度
int size[MAXN];//子树大小
int pos[MAXN];//dfs序
int son[MAXN];//重儿子是哪个
int SEG;//dfs序当前点
void init(){
edge_num=0;
memset(head,-1,sizeof(head));
SEG=1;
memset(son,-1,sizeof(son));
}
//求出fa,deep,num,son
void dfs1(int u,int pre,int d){
deep[u]=d;
fa[u]=pre;
size[u]=1;
for(int i=head[u];i!=-1;i=e[i].next){
int v=e[i].to;
if(v!=pre){
dfs1(v,u,d+1);
size[u]+=size[v];
if(son[u]==-1||size[v]>size[son[u]])
son[u]=v;
}
}
}
//求出top和pos,求的过程中,先求重链上的dfs序
void dfs2(int u,int sp){
top[u]=sp;
if(son[u]!=-1){
pos[u]=SEG++;
dfs2(son[u],sp);
}
else{
pos[u]=SEG++;
return;
}
for(int i=head[u];i!=-1;i=e[i].next){
int v=e[i].to;
if(v!=son[u]&&v!=fa[u]){
dfs2(v,v);
}
}
}
//线段树部分
int tree[MAXN<<2];//线段树数组,看你要存什么
int tree2[MAXN<<2];//线段树数组,看你要存什么
int Add[MAXN<<2];//懒惰标记,这里是区间累加
int n;
int A[MAXN];//原数组,下标1~n
//更新节点信息,这里是求最值
void pushup(int rt){
tree[rt]=max(tree[rt<<1],tree[rt<<1|1]);//<<1为*2.<<1|1为*2+1,即左子树和右子树
tree2[rt]=min(tree2[rt<<1],tree2[rt<<1|1]);//<<1为*2.<<1|1为*2+1,即左子树和右子树
}
//建树
void build(int l,int r,int rt){//l,r表示当前区间,rt表示当前区间在线段树数组中的位置
Add[rt]=0;
tree[rt]=-INF;//将该位置存原数组的值
tree2[rt]=INF;//将该位置存原数组的值
if(l==r)return;
int m=(l+r)>>1;//>>1等于/2
//递归建树
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
pushup(rt);//建完左右子树后,更新当前节点的值
}
//下推函数,ln,rn为左子树,右子树的数字数量。
void pushdown(int rt,int ln,int rn){
if(Add[rt]){//如果存在下推标记
//将标记下推至子节点
Add[rt<<1]^=1;
Add[rt<<1|1]^=1;
//修改子节点的值
tree[rt<<1]=-tree[rt<<1];
tree2[rt<<1]=-tree2[rt<<1];
swap(tree[rt<<1],tree2[rt<<1]);
tree[rt<<1|1]=-tree[rt<<1|1];
tree2[rt<<1|1]=-tree2[rt<<1|1];
swap(tree[rt<<1|1],tree2[rt<<1|1]);
//清除标记
Add[rt]=0;
}
}
//点修改,即A[L]=C,要同时修改相关区间的值,与建树同理,其实就是建树的过程
void update(int L,int C,int l,int r,int rt){
if(l==r){//若到达叶节点,则修改叶节点的值
tree[rt]=C;
tree2[rt]=C;
Add[rt]=0;
return;
}
int m=(l+r)>>1;
pushdown(rt,m-l+1,r-m);//必须下推标记,因为有pushup
//根据L判断是往哪个子树递归修改
if(L<=m)
update(L,C,l,m,rt<<1);//左子树
else
update(L,C,m+1,r,rt<<1|1);//右子树
pushup(rt);//子节点更新完了,那么可以更新自己了,即从下而上修改,建树同理
}
//区间修改,比点修改多了个R参数,这里是区间+C,与查询同理,在查询时修改
void update(int L,int R,int C, int l, int r, int rt){
if(L<=l&&r<=R){//如果当前区间在查询区间内,直接对其进行修改
tree[rt]=-tree[rt];//C*当前区间含有的数字个数
tree2[rt]=-tree2[rt];//C*当前区间含有的数字个数
swap(tree[rt],tree2[rt]);
Add[rt]^=1;//增加Add标记,表示本区间的值正确,子区间的值仍需要根据Add的值来调整
return;
}
int m=(l+r)>>1;
pushdown(rt,m-l+1,r-m);//必须下推标记,因为有pushup
if(L <= m) //与查询同理
update(L,R,C,l,m,rt<<1);
if(R > m)
update(L,R,C,m+1,r,rt<<1|1);
pushup(rt);//更新本节点信息
}
//查询,这里为求最值,LR代表要查询的区间,lr代表当前区间,rt表示当前节点在数组中的实际位置
int query(int L,int R,int l,int r,int rt){
if(L<=l&&r<=R)//如果当前区间在查询区间内,直接返回当前存的值
return tree[rt];
int m=(l+r)>>1;
//每次查询都下推标记,保证值正确
pushdown(rt,m-l+1,r-m);
//累加求答案
int ANS=-INF;
if(L<=m)//如果左子区间与[L,R]有重叠,就递归左子树,右子树同理。
ANS=max(ANS,query(L,R,l,m,rt<<1));
if(R>m)
ANS=max(ANS,query(L,R,m+1,r,rt<<1|1));
return ANS;
}
//线段树+树链剖分部分
int queryMAX(int u,int v){
int f1=top[u],f2=top[v];
int temp=-INF;
while(f1!=f2){
if(deep[f1]<deep[f2]){
swap(f1,f2);
swap(u,v);
}
temp=max(temp,query(pos[f1],pos[u],1,n,1));
u=fa[f1];
f1=top[u];
}
if(u==v)
return temp==-INF?0:temp;
if(deep[u]>deep[v])
swap(u,v);
return max(temp,query(pos[son[u]],pos[v],1,n,1));
}
void update(int u,int v){
int f1=top[u],f2=top[v];
while(f1!=f2){
if(deep[f1]<deep[f2]){
swap(f1,f2);
swap(u,v);
}
update(pos[f1],pos[u],1,1,n,1);
u=fa[f1];
f1=top[u];
}
if(u==v)
return;
if(deep[u]>deep[v])
swap(u,v);
update(pos[son[u]],pos[v],1,1,n,1);
}
int in[MAXN][3];
int main(){
int q;
int t;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
init();
int u,v;
for(int i=0;i<n-1;i++){
scanf("%d%d%d",&in[i][0],&in[i][1],&in[i][2]);
insert_edge(in[i][0],in[i][1]);
insert_edge(in[i][1],in[i][0]);
}
dfs1(1,0,0);
dfs2(1,1);
build(1,n,1);
for(int i=0;i<n-1;i++){
if(deep[in[i][0]]<deep[in[i][1]])
swap(in[i][0],in[i][1]);
update(pos[in[i][0]],in[i][2],1,n,1);
}
char op[10];
while(1){
scanf("%s",op);
if(op[0]=='D')
break;
scanf("%d%d",&u,&v);
if(op[0]=='Q'){
printf("%d\n",queryMAX(u,v));
}
else{
if(op[0]=='N'){
update(u,v);
}
else
update(pos[in[u-1][0]],v,1,n,1);
}
}
}
return 0;
}
Description
You are given a tree with N nodes. The tree’s nodes are numbered 1 through N and its edges are numbered 1 through N − 1. Each edge is associated with a weight. Then you are to execute a series of instructions on the tree. The instructions
can be one of the following forms:
CHANGEi v | Change the weight of the ith edge to v |
NEGATEa b | Negate the weight of every edge on the path from a to b |
QUERYa b | Find the maximum weight of edges on the path from a to b |
The input contains multiple test cases. The first line of input contains an integer t (t ≤ 20), the number of test cases. Then follow the test cases.
Each test case is preceded by an empty line. The first nonempty line of its contains N (N ≤ 10,000). The next N − 1 lines each contains three integers a, b and c, describing an edge connecting nodes a and b with
weight c. The edges are numbered in the order they appear in the input. Below them are the instructions, each sticking to the specification above. A lines with the word “
DONE” ends the test case.
Output
For each “
QUERY” instruction, output the result on a separate line.
Sample Input
1 3 1 2 1 2 3 2 QUERY 1 2 CHANGE 1 3 QUERY 1 2 DONE
Sample Output
1 3
题意:给你一个树和三种操作,分别为求最大权值,单点更新,区间更新
解题思路:negate是取反的意思,所以要记录最大值和最小值……其他就套模板了。
#include<iostream>
#include<deque>
#include<memory.h>
#include<stdio.h>
#include<map>
#include<string>
#include<algorithm>
#include<vector>
#include<math.h>
#include<stack>
#include<queue>
#include<bitset>
#include<set>
#define INF (0x3f3f3f3f)
using namespace std;
typedef long long int ll;
const int MAXN=100010;
//邻接图部分
struct Edge{
int to;
int next;
}e[MAXN*2];
int head[MAXN],edge_num;
void insert_edge(int u,int v){
e[edge_num].to=v;
e[edge_num].next=head[u];
head[u]=edge_num++;
}
//树链剖分部分
int top[MAXN];//重链顶点
int fa[MAXN];//父亲节点
int deep[MAXN];//深度
int size[MAXN];//子树大小
int pos[MAXN];//dfs序
int son[MAXN];//重儿子是哪个
int SEG;//dfs序当前点
void init(){
edge_num=0;
memset(head,-1,sizeof(head));
SEG=1;
memset(son,-1,sizeof(son));
}
//求出fa,deep,num,son
void dfs1(int u,int pre,int d){
deep[u]=d;
fa[u]=pre;
size[u]=1;
for(int i=head[u];i!=-1;i=e[i].next){
int v=e[i].to;
if(v!=pre){
dfs1(v,u,d+1);
size[u]+=size[v];
if(son[u]==-1||size[v]>size[son[u]])
son[u]=v;
}
}
}
//求出top和pos,求的过程中,先求重链上的dfs序
void dfs2(int u,int sp){
top[u]=sp;
if(son[u]!=-1){
pos[u]=SEG++;
dfs2(son[u],sp);
}
else{
pos[u]=SEG++;
return;
}
for(int i=head[u];i!=-1;i=e[i].next){
int v=e[i].to;
if(v!=son[u]&&v!=fa[u]){
dfs2(v,v);
}
}
}
//线段树部分
int tree[MAXN<<2];//线段树数组,看你要存什么
int tree2[MAXN<<2];//线段树数组,看你要存什么
int Add[MAXN<<2];//懒惰标记,这里是区间累加
int n;
int A[MAXN];//原数组,下标1~n
//更新节点信息,这里是求最值
void pushup(int rt){
tree[rt]=max(tree[rt<<1],tree[rt<<1|1]);//<<1为*2.<<1|1为*2+1,即左子树和右子树
tree2[rt]=min(tree2[rt<<1],tree2[rt<<1|1]);//<<1为*2.<<1|1为*2+1,即左子树和右子树
}
//建树
void build(int l,int r,int rt){//l,r表示当前区间,rt表示当前区间在线段树数组中的位置
Add[rt]=0;
tree[rt]=-INF;//将该位置存原数组的值
tree2[rt]=INF;//将该位置存原数组的值
if(l==r)return;
int m=(l+r)>>1;//>>1等于/2
//递归建树
build(l,m,rt<<1);
build(m+1,r,rt<<1|1);
pushup(rt);//建完左右子树后,更新当前节点的值
}
//下推函数,ln,rn为左子树,右子树的数字数量。
void pushdown(int rt,int ln,int rn){
if(Add[rt]){//如果存在下推标记
//将标记下推至子节点
Add[rt<<1]^=1;
Add[rt<<1|1]^=1;
//修改子节点的值
tree[rt<<1]=-tree[rt<<1];
tree2[rt<<1]=-tree2[rt<<1];
swap(tree[rt<<1],tree2[rt<<1]);
tree[rt<<1|1]=-tree[rt<<1|1];
tree2[rt<<1|1]=-tree2[rt<<1|1];
swap(tree[rt<<1|1],tree2[rt<<1|1]);
//清除标记
Add[rt]=0;
}
}
//点修改,即A[L]=C,要同时修改相关区间的值,与建树同理,其实就是建树的过程
void update(int L,int C,int l,int r,int rt){
if(l==r){//若到达叶节点,则修改叶节点的值
tree[rt]=C;
tree2[rt]=C;
Add[rt]=0;
return;
}
int m=(l+r)>>1;
pushdown(rt,m-l+1,r-m);//必须下推标记,因为有pushup
//根据L判断是往哪个子树递归修改
if(L<=m)
update(L,C,l,m,rt<<1);//左子树
else
update(L,C,m+1,r,rt<<1|1);//右子树
pushup(rt);//子节点更新完了,那么可以更新自己了,即从下而上修改,建树同理
}
//区间修改,比点修改多了个R参数,这里是区间+C,与查询同理,在查询时修改
void update(int L,int R,int C, int l, int r, int rt){
if(L<=l&&r<=R){//如果当前区间在查询区间内,直接对其进行修改
tree[rt]=-tree[rt];//C*当前区间含有的数字个数
tree2[rt]=-tree2[rt];//C*当前区间含有的数字个数
swap(tree[rt],tree2[rt]);
Add[rt]^=1;//增加Add标记,表示本区间的值正确,子区间的值仍需要根据Add的值来调整
return;
}
int m=(l+r)>>1;
pushdown(rt,m-l+1,r-m);//必须下推标记,因为有pushup
if(L <= m) //与查询同理
update(L,R,C,l,m,rt<<1);
if(R > m)
update(L,R,C,m+1,r,rt<<1|1);
pushup(rt);//更新本节点信息
}
//查询,这里为求最值,LR代表要查询的区间,lr代表当前区间,rt表示当前节点在数组中的实际位置
int query(int L,int R,int l,int r,int rt){
if(L<=l&&r<=R)//如果当前区间在查询区间内,直接返回当前存的值
return tree[rt];
int m=(l+r)>>1;
//每次查询都下推标记,保证值正确
pushdown(rt,m-l+1,r-m);
//累加求答案
int ANS=-INF;
if(L<=m)//如果左子区间与[L,R]有重叠,就递归左子树,右子树同理。
ANS=max(ANS,query(L,R,l,m,rt<<1));
if(R>m)
ANS=max(ANS,query(L,R,m+1,r,rt<<1|1));
return ANS;
}
//线段树+树链剖分部分
int queryMAX(int u,int v){
int f1=top[u],f2=top[v];
int temp=-INF;
while(f1!=f2){
if(deep[f1]<deep[f2]){
swap(f1,f2);
swap(u,v);
}
temp=max(temp,query(pos[f1],pos[u],1,n,1));
u=fa[f1];
f1=top[u];
}
if(u==v)
return temp==-INF?0:temp;
if(deep[u]>deep[v])
swap(u,v);
return max(temp,query(pos[son[u]],pos[v],1,n,1));
}
void update(int u,int v){
int f1=top[u],f2=top[v];
while(f1!=f2){
if(deep[f1]<deep[f2]){
swap(f1,f2);
swap(u,v);
}
update(pos[f1],pos[u],1,1,n,1);
u=fa[f1];
f1=top[u];
}
if(u==v)
return;
if(deep[u]>deep[v])
swap(u,v);
update(pos[son[u]],pos[v],1,1,n,1);
}
int in[MAXN][3];
int main(){
int q;
int t;
scanf("%d",&t);
while(t--){
scanf("%d",&n);
init();
int u,v;
for(int i=0;i<n-1;i++){
scanf("%d%d%d",&in[i][0],&in[i][1],&in[i][2]);
insert_edge(in[i][0],in[i][1]);
insert_edge(in[i][1],in[i][0]);
}
dfs1(1,0,0);
dfs2(1,1);
build(1,n,1);
for(int i=0;i<n-1;i++){
if(deep[in[i][0]]<deep[in[i][1]])
swap(in[i][0],in[i][1]);
update(pos[in[i][0]],in[i][2],1,n,1);
}
char op[10];
while(1){
scanf("%s",op);
if(op[0]=='D')
break;
scanf("%d%d",&u,&v);
if(op[0]=='Q'){
printf("%d\n",queryMAX(u,v));
}
else{
if(op[0]=='N'){
update(u,v);
}
else
update(pos[in[u-1][0]],v,1,n,1);
}
}
}
return 0;
}
相关文章推荐
- POJ - 3237 Tree(树剖 + 区间修改)
- poj 3237 Tree 【LCA转RMQ】 【修改边权 + 修改(a, b)路径边权 + 查询(a, b)路径上最大边权值】
- POJ 3237 Tree(树链剖分 + 单点更新 + 区间更新 + 区间查询)
- POJ 3237 Tree(树链剖分 线段树区间标记)
- POJ 3237 Tree(树链剖分-线段树点更新-区间更新-区间最值查询-入边)
- poj 2777 Count Color(线段树区间修改)
- (POJ 3468)A Simple Problem with Integers 线段树区间修改入门讲解
- POJ-2104 K-th Number (主席树 不带修改区间第k大)
- poj 3468 块状链表 区间修改+区间查询
- |poj 3237|树链剖分|线段树|Tree
- POJ 2528 Mayor's posters(线段树区间修改+离散化)
- poj 3237 Tree(最近公共祖先)
- 【POJ】3237 Tree
- 模板(线段树 + 树状数组 + 区间修改 + 区间查询)eg:POJ 3468 - A Simple Problem with Integers
- 【SPOJ 375】 【POJ 3237】 375. Query on a tree 树链剖分
- POJ 3237 Tree (树链剖分+线段树)
- POJ 3468 线段树区间修改查询(Java,c++实现)
- 【POJ】3237 Tree 树链剖分
- Splay_Tree 模板(区间修改,旋转操作)
- poj 3468 线段数 修改区间(点)