入门的话,这篇还是写的不错的:
http://blog.sina.com.cn/s/blog_6974c8b20100zc61.html
一个入门小专题:https://vjudge.net/contest/158100#overview
1.SPOJ QTREE 树链剖分+线段树
题意:给两个操作,一个是把第i条边权值修改成ti,另一个是查询a到b之间最大的边权值
基于边权,修改单条边权,查询路径边权最大值
代码:
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <string>
#include <vector>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string.h>
#include <algorithm>
using namespace std;
#define ff first
#define ss second
#define pb push_back
#define ll long long
#define mod 1000000007
#define ull unsigned long long
#define mst(ss,b) memset(ss,b,sizeof(ss));
#define dbg(x) cout << #x << "= " << x << endl;
#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
const int inf = 0x3f3f3f3f;
const int N = 5e4+5;
int n, m, q;
struct Edge{
int to, next;
}edge[N<<1];
int head[N], tol;
int top[N];//top[v]表示v所在的重链的顶端节点
int fa[N];//父亲节点
int dep[N];//深度
int sz[N];//sz[v]表示以v为根的子树的节点数
int p[N];//p[v]表示v与其父亲节点的连边在线段树中的位置
int fp[N];//和p数组相反 已知v在线段树中位置,其父亲节点是u
int son[N];//重儿子
int pos;
int a[N];
void init(){
tol = 0;
memset(head, -1, sizeof(head));
pos = 0;
memset(son, -1, sizeof(son));
}
inline void addedge(int u, int v){
edge[tol].to = v;edge[tol].next = head[u]; head[u] = tol++;
}
void dfs1(int u, int pre, int d){//第一遍dfs求出fa,deep,sz,son
dep[u] = d;
fa[u] = pre;
sz[u] = 1;
for(int i=head[u]; i!=-1; i=edge[i].next){
int v = edge[i].to;
if(v != pre){
dfs1(v, u, d+1);
sz[u] += sz[v];
if(son[u] == -1 || sz[v] > sz[son[u]])
son[u] = v;
}
}
}
void getpos(int u, int sp){ //第二遍dfs求出top和p
top[u] = sp;
p[u] = ++pos; //树状数组编号从1开始
fp[p[u]] = u;
if(son[u] == -1)return ;
getpos(son[u], sp);
for(int i=head[u]; i!=-1; i=edge[i].next){
int v = edge[i].to;
if(v != son[u] && v != fa[u])
getpos(v, v); //轻儿子
}
}
struct BIT{
int n;
int c[N];
void init(int _n){
n = _n;
for(int i=1; i<=n; i++)c[i] = 0;
mst(c, 0);
}
void update(int p, int x){
for(int i=p; i<=n; i+=i&-i)c[i] += x;
}
int query(int p){
int ret = 0;
for(int i=p; i>0; i-=i&-i)ret += c[i];
return ret;
}
}bit;
void solve(int u, int v, int val){
int f1 = top[u], f2 = top[v];
int tmp = 0;
while(f1 != f2){
if(dep[f1] < dep[f2]){
swap(f1, f2);
swap(u, v);
}
bit.update(p[f1], val);
bit.update(p[u]+1, -val);
u = fa[f1]; f1 = top[u];
}
//if(u == v)return ; 加了就wa了,因为树状数组区间修改不能少了下面的操作
if(dep[u] > dep[v])swap(u, v);
bit.update(p[u], val);
bit.update(p[v]+1, -val);
}
int main(){
while(~scanf("%d%d%d", &n, &m, &q)){
init();
for(int i=1; i<=n; i++)scanf("%d", &a[i]);
for(int i=1; i<=m; i++){
int u, v;
scanf("%d%d", &u, &v);
addedge(u, v);
addedge(v, u);
}
dfs1(1, -1, 0);
getpos(1, 1);
bit.init(pos);
for(int i=1; i<=n; i++){
bit.update(p[i], a[i]);
bit.update(p[i]+1, -a[i]);
}
while(q--){
char op[10];
scanf("%s", op);
if(op[0] == 'Q'){
int C;
scanf("%d", &C);
printf("%d\n", bit.query(p[C]));
}
else{
int C1, C2, K;
scanf("%d%d%d", &C1, &C2, &K);
if(op[0] == 'D')K = -K;
solve(C1, C2, K);
}
}
}
return 0;
}
2.HDU 3966 Aragorn’s Story (树链剖分+树状数组)
题意:
给一棵树,并给定各个点权的值,然后有3种操作:
I C1 C2 K: 把C1与C2的路径上的所有点权值加上K
D C1 C2 K:把C1与C2的路径上的所有点权值减去K
Q C:查询节点编号为C的权值
基于点权,查询单点值,修改路径的上的点权
树状数组区间修改,单点查询可以参考这篇文章:
http://blog.youkuaiyun.com/kenden23/article/details/29854527
代码:
#include <set>
#include <map>
#include <stack>
#include <queue>
#include <string>
#include <vector>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <iostream>
#include <string.h>
#include <algorithm>
using namespace std;
#define ff first
#define ss second
#define pb push_back
#define ll long long
#define mod 1000000007
#define ull unsigned long long
#define mst(ss,b) memset(ss,b,sizeof(ss));
#define dbg(x) cout << #x << "= " << x << endl;
#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
const int inf = 0x3f3f3f3f;
const int N = 5e4+5;
int n, m, q;
struct Edge{
int to, next;
}edge[N<<1];
int head[N], tol;
int top[N];//top[v]表示v所在的重链的顶端节点
int fa[N];//父亲节点
int dep[N];//深度
int sz[N];//sz[v]表示以v为根的子树的节点数
int p[N];//p[v]表示v与其父亲节点的连边在线段树中的位置
int fp[N];//和p数组相反 已知v在线段树中位置,其父亲节点是u
int son[N];//重儿子
int pos;
int a[N];
void init(){
tol = 0;
memset(head, -1, sizeof(head));
pos = 0;
memset(son, -1, sizeof(son));
}
inline void addedge(int u, int v){
edge[tol].to = v;edge[tol].next = head[u]; head[u] = tol++;
}
void dfs1(int u, int pre, int d){//第一遍dfs求出fa,deep,sz,son
dep[u] = d;
fa[u] = pre;
sz[u] = 1;
for(int i=head[u]; i!=-1; i=edge[i].next){
int v = edge[i].to;
if(v != pre){
dfs1(v, u, d+1);
sz[u] += sz[v];
if(son[u] == -1 || sz[v] > sz[son[u]])
son[u] = v;
}
}
}
void getpos(int u, int sp){ //第二遍dfs求出top和p
top[u] = sp;
p[u] = ++pos; //树状数组编号从1开始
fp[p[u]] = u;
if(son[u] == -1)return ;
getpos(son[u], sp);
for(int i=head[u]; i!=-1; i=edge[i].next){
int v = edge[i].to;
if(v != son[u] && v != fa[u])
getpos(v, v); //轻儿子
}
}
struct BIT{
int n;
int c[N];
void init(int _n){
n = _n;
for(int i=1; i<=n; i++)c[i] = 0;
mst(c, 0);
}
void update(int p, int x){
for(int i=p; i<=n; i+=i&-i)c[i] += x;
}
int query(int p){
int ret = 0;
for(int i=p; i>0; i-=i&-i)ret += c[i];
return ret;
}
}bit;
void solve(int u, int v, int val){
int f1 = top[u], f2 = top[v];
int tmp = 0;
while(f1 != f2){
if(dep[f1] < dep[f2]){
swap(f1, f2);
swap(u, v);
}
bit.update(p[f1], val);
bit.update(p[u]+1, -val);
u = fa[f1]; f1 = top[u];
}
//if(u == v)return ; 加了就wa了,因为树状数组区间修改不能少了下面的操作
if(dep[u] > dep[v])swap(u, v);
bit.update(p[u], val);
bit.update(p[v]+1, -val);
}
int main(){
while(~scanf("%d%d%d", &n, &m, &q)){
init();
for(int i=1; i<=n; i++)scanf("%d", &a[i]);
for(int i=1; i<=m; i++){
int u, v;
scanf("%d%d", &u, &v);
addedge(u, v);
addedge(v, u);
}
dfs1(1, -1, 0);
getpos(1, 1);
bit.init(pos);
for(int i=1; i<=n; i++){
bit.update(p[i], a[i]);
bit.update(p[i]+1, -a[i]);
}
while(q--){
char op[10];
scanf("%s", op);
if(op[0] == 'Q'){
int C;
scanf("%d", &C);
printf("%d\n", bit.query(p[C]));
}
else{
int C1, C2, K;
scanf("%d%d%d", &C1, &C2, &K);
if(op[0] == 'D')K = -K;
solve(C1, C2, K);
}
}
}
return 0;
}
3.POJ3237 Tree
题意:
一棵树有n个节点,n-1条边,每条边有一个权值,三种操作
CHANGE i v:将第i条边的权值改成v
NEGATE a b:将a到b路径上所有边的权值取反(x->-x)
QUERY a b:查询a到b路径上所有边权值的最大值
思路:
树链剖分,然后建线段树,由于此题有取反操作,所以树中元素不仅要存区间最大值,还要存区间最小值,同时需要一个lazy表示该区间是否被取反,之后就是线段树单点修改、区间更新以及区间查询问题了
代码:
#include <set>
#include <map>
#include <queue>
#include <vector>
#include <math.h>
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std;
#define ff first
#define ss second
#define pb push_back
#define ll long long
#define mod 1000000007
#define ull unsigned long long
#define min3(a, b, c) min(a, min(b, c))
#define max3(a, b, c) max(a, max(b, c))
#define mst(ss,b) memset(ss,b,sizeof(ss));
#define dbg(x) cout << #x << "= " << x << endl;
#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
typedef pair <int, int> pii;
const int inf = 0x3f3f3f3f;
const ll INF = (1LL<<63)-1;
const int N = 1e4+5;
struct Edge{
int to, next;
}edge[N<<1];
int head[N], tol;
int top[N];//top[v]表示v所在的重链的顶端节点
int fa[N];//父亲节点
int dep[N];//深度
int sz[N];//sz[v]表示以v为根的子树的节点数
int p[N];//p[v]表示v与其父亲节点的连边在线段树中的位置
int fp[N];//和p数组相反 已知v在线段树中位置,其父亲节点是u
int son[N];//重儿子
int pos; //pos为线段树或者树状数组的大小
void init(){
tol = 0;
memset(head, -1, sizeof(head));
pos = 0;
memset(son, -1, sizeof(son));
}
void addedge(int u, int v){
edge[tol].to = v;edge[tol].next = head[u]; head[u] = tol++;
}
void dfs1(int u, int pre, int d){//第一遍dfs求出fa,deep,sz,son
dep[u] = d;
fa[u] = pre;
sz[u] = 1;
for(int i=head[u]; i!=-1; i=edge[i].next){
int v = edge[i].to;
if(v != pre){
dfs1(v, u, d+1);
sz[u] += sz[v];
if(son[u] == -1 || sz[v] > sz[son[u]])
son[u] = v;
}
}
}
void getpos(int u, int sp){ //第二遍dfs求出top和p
top[u] = sp;
p[u] = ++pos;
fp[p[u]] = u;
if(son[u] == -1)return ;
getpos(son[u], sp);
for(int i=head[u]; i!=-1; i=edge[i].next){
int v = edge[i].to;
if(v != son[u] && v != fa[u])
getpos(v, v); //轻儿子
}
}
int Max[N<<2];
int Min[N<<2];
int lazy[N<<2];
void PushUp(int rt){
Max[rt] = max(Max[rt<<1], Max[rt<<1|1]);
Min[rt] = min(Min[rt<<1], Min[rt<<1|1]);
}
void change(int rt){
Max[rt] = -Max[rt];
Min[rt] = -Min[rt];
swap(Max[rt], Min[rt]);
}
void PushDown(int rt){
if(lazy[rt]){
lazy[rt] ^= 1;
lazy[rt<<1] ^= 1;
lazy[rt<<1|1] ^= 1;
change(rt<<1);
change(rt<<1|1);
}
}
void build(int l, int r, int rt){
if(l == r){
// Max[rt] = 0;
// Min[rt] = 0;
// lazy[rt] = 0;
return ;
}
int m = (l+r)>>1;
build(lson);
build(rson);
PushUp(rt);
}
void update(int p, int re, int l, int r, int rt){
if(l == r){
Max[rt] = Min[rt] = re;
return ;
}
PushDown(rt);
int m = (l+r)>>1;
if(p <= m)update(p, re, lson);
else update(p, re, rson);
PushUp(rt);
}
void ne_update(int L, int R, int l, int r, int rt){
if(L <= l && r <= R){
lazy[rt] ^= 1;
change(rt);
return ;
}
PushDown(rt);
int m = (l+r)>>1;
if(L <= m)ne_update(L, R, lson);
if(R > m)ne_update(L, R, rson);
PushUp(rt);
}
int query(int L, int R, int l, int r, int rt){
if(L <= l && r <= R){
return Max[rt];
}
PushDown(rt);
int m = (l+r)>>1;
int ret = -inf; 初始化
if(L <= m)ret = max(ret, query(L, R, lson));
if(R > m)ret = max(ret, query(L, R, rson));
return ret;
}
int getmax(int u, int v){
int f1 = top[u], f2 = top[v];
int tmp = -inf; 初始化
while(f1 != f2){
if(dep[f1] < dep[f2]){
swap(f1, f2);
swap(u, v);
}
tmp = max(tmp, query(p[f1], p[u], 1, pos, 1));
u = fa[f1]; f1 = top[u];
}
if(u == v)return tmp;
if(dep[u] >dep[v])swap(u, v);
return max(tmp, query(p[son[u]], p[v], 1, pos, 1));
}
void Negate(int u, int v){
int f1 = top[u], f2 = top[v];
while(f1 != f2){
if(dep[f1] < dep[f2]){
swap(f1, f2);
swap(u, v);
}
ne_update(p[f1], p[u], 1, pos, 1);
u = fa[f1]; f1 = top[u];
}
if(u == v)return ;
if(dep[u] > dep[v])swap(u, v);
return ne_update(p[son[u]], p[v], 1, pos, 1);
}
int e[N][3];
int main(){
int T;
scanf("%d", &T);
while(T--){
init();
int n;
scanf("%d", &n);
for(int i=1; i<n; i++){
for(int j=0; j<3; j++){
scanf("%d", &e[i][j]);
}
addedge(e[i][0], e[i][1]);
addedge(e[i][1], e[i][0]);
}
dfs1(1, -1, 0);
getpos(1, 1);
build(1, pos, 1);
for(int i=1; i<n; i++){
if(dep[e[i][0]] > dep[e[i][1]])swap(e[i][0], e[i][1]);
update(p[e[i][1]], e[i][2], 1, pos, 1);
}
char op[10];
while(~scanf("%s", op)){
if(op[0] == 'D')break;
int u, v;
scanf("%d%d", &u, &v);
if(op[0] == 'C')update(p[e[u][1]], v, 1, pos, 1);
else if(op[0] == 'Q')printf("%d\n", getmax(u, v));
else Negate(u, v);
}
}
return 0;
}
4.2017川大邀请赛A题 scu4549 Greed King
题意:一颗树支持两种操作:
1.给点a到b之间所有的边的边权增加c
2.查询点a到b之间的边权和
思路:简单的树链剖分,用线段树维护区间增和区间查询操作
代码:
#include <set>
#include <map>
#include <queue>
#include <vector>
#include <math.h>
#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <string.h>
using namespace std;
#define ff first
#define ss second
#define pb push_back
#define ll long long
#define mod 1000000007
#define ull unsigned long long
#define min3(a, b, c) min(a, min(b, c))
#define max3(a, b, c) max(a, max(b, c))
#define mst(ss,b) memset(ss,b,sizeof(ss));
#define dbg(x) cout << #x << "= " << x << endl;
#define lson l, m, rt<<1
#define rson m+1, r, rt<<1|1
typedef pair <int, int> pii;
const int inf = 0x3f3f3f3f;
const ll INF = (1LL<<63)-1;
const int N = 1e5+5;
struct node{
int to, next;
}edge[N<<1];
int head[N], tol;
int top[N];//top[v]表示v所在的重链的顶端节点
int fa[N];//父亲节点
int dep[N];//深度
int sz[N];//sz[v]表示以v为根的子树的节点数
int p[N];//p[v]表示v与其父亲节点的连边在线段树中的位置
int fp[N];//和p数组相反 已知v在线段树中位置,其父亲节点是u
int son[N];//重儿子
int pos; //pos为线段树或者树状数组的大小
void init(){
tol = 0;
memset(head, -1, sizeof(head));
pos = 0;
memset(son, -1, sizeof(son));
}
void addedge(int u, int v){
edge[tol].to = v;edge[tol].next = head[u]; head[u] = tol++;
}
void dfs1(int u, int pre, int d){//第一遍dfs求出fa,deep,sz,son
dep[u] = d;
fa[u] = pre;
sz[u] = 1;
for(int i=head[u]; i!=-1; i=edge[i].next){
int v = edge[i].to;
if(v != pre){
dfs1(v, u, d+1);
sz[u] += sz[v];
if(son[u] == -1 || sz[v] > sz[son[u]])
son[u] = v;
}
}
}
void getpos(int u, int sp){ //第二遍dfs求出top和p
top[u] = sp;
p[u] = ++pos;
fp[p[u]] = u;
if(son[u] == -1)return ;
getpos(son[u], sp);
for(int i=head[u]; i!=-1; i=edge[i].next){
int v = edge[i].to;
if(v != son[u] && v != fa[u])
getpos(v, v); //轻儿子
}
}
ll sum[N<<2];初始化
ll add[N<<2];
void PushUp(int rt){
sum[rt] = sum[rt<<1]+sum[rt<<1|1];
}
void PushDown(int rt, int m){
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;
}
}
void build(int p, int re, int l, int r, int rt){
add[rt] = 0;
sum[rt] = 0;
if(l == r){
sum[rt] = re;
return ;
}
int m=(l+r)>>1;
if(p<=m)build(p, re, lson);
else build(p, re, rson);
PushUp(rt);
}
void update(int L, int R, int c, int l, int r, int rt){
if(L <= l && r <= R){
add[rt] += c;
sum[rt] += (ll)c*(r-l+1);
return ;
}
PushDown(rt, r-l+1);
int m = (l+r)>>1;
if(L <= m)update(L, R, c, lson);
if(R > m)update(L, R, c, rson);
PushUp(rt);
}
ll query(int L, int R, int l, int r, int rt){
if(L <= l && r <= R){
return sum[rt];
}
PushDown(rt, r-l+1);
int m = (l+r)>>1;
ll ret = 0;
if(L <= m)ret += query(L, R, lson);
if(R > m)ret += query(L, R, rson);
return ret;
}
ll queryab(int u, int v){
int f1 = top[u], f2 = top[v];
ll tmp = 0;
while(f1 != f2){
if(dep[f1] < dep[f2]){ //对深度大的操作
swap(f1, f2);
swap(u, v);
}
tmp += query(p[f1], p[u], 1, pos, 1);
u = fa[f1]; f1 = top[u];
}
if(u == v)return tmp;
if(dep[u] > dep[v])swap(u, v); //最后一次操作
tmp += query(p[son[u]], p[v], 1, pos, 1);
return tmp;
}
void updateab(int u, int v, int x){
int f1 = top[u], f2 = top[v];
while(f1 != f2){
if(dep[f1] < dep[f2]){ //对深度大的操作
swap(f1, f2);
swap(u, v);
}
update(p[f1], p[u], x, 1, pos, 1);
u = fa[f1]; f1 = top[u];
}
if(u == v)return ;
if(dep[u] > dep[v])swap(u, v); //最后一次操作
update(p[son[u]], p[v], x, 1, pos, 1);
}
int e[N][3];
int main(){
int T;
scanf("%d", &T);
while(T--){
init();
// mst(sum, 0); //初始化
int n, q;
scanf("%d%d", &n, &q);
for(int i=1; i<n; i++){
for(int j=0; j<3; j++)scanf("%d", &e[i][j]);
addedge(e[i][0], e[i][1]);
addedge(e[i][1], e[i][0]);
}
dfs1(1, -1, 0);
getpos(1, 1);
for(int i=1; i<n; i++){
if(dep[e[i][0]] > dep[e[i][1]])swap(e[i][0], e[i][1]);
build(p[e[i][1]], e[i][2], 1, pos, 1);
}
char op[10];
while(q--){
scanf("%s", op);
if(op[0] == 'Q'){
int u, v;
scanf("%d%d", &u, &v);
printf("%lld\n", queryab(u, v));
}
else{
int u, v, x;
scanf("%d%d%d", &u, &v, &x);
updateab(u, v, x);
}
}
}
return 0;
}