poj 2763(LCA+树状数组/树链剖分+线段树)

poj 2763

LCA+树状数组/线段树。

首先本题大致一看就是一个LCA,但是本题有操作更改某边的权,这样会使得原本的far数组变化,不难发现,更改边权后影响该边下面所有点的答案。此时可以在LCA的DFS预处理时求出DFS序列(即时间戳),找到每个点管辖的范围,修改边权相当于修改该边连接的两个点深度深的那个点所管辖的范围,此时修改可以用暴力,但是由于far数组与LCA本身查询无关,我们可以用数据结构进行维护far数组,树状数组/线段树都可以

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#define ms(i, j) memset(i, j, sizeof i)
#define FN2 "poj2763"
using namespace std;
const int MAXN = 100000 + 5, logs = 17;
int n, q, s, wi[MAXN], deep[MAXN], pre[MAXN][logs+1], far[MAXN], ll[MAXN], rr[MAXN], cnt, a[MAXN], b[MAXN], Sc[MAXN];
vector<int> G[MAXN];
//bit
int lowbit(int x)
{
return x&(-x);
}
void update(int x, int v)
{
for (int i=x;i<=n;i+=lowbit(i))
{
far[i] += v;
}
}
int dist(int x)
{
int ret = 0;
for (int i=x;i>0;i-=lowbit(i))
{
ret += far[i];
}
return ret;
}
//lca
void dfs(int x, int p)
{
ll[x] = ++cnt;
Sc[x] = cnt;
pre[x][0] = p;
deep[x] = deep[p] + 1;
for (int i=1;i<=logs;i++) pre[x][i] = pre[pre[x][i-1]][i-1];
for (int i=0;i<G[x].size();i++)
{
int u = G[x][i];
if (u!=p)
{
dfs(u, x);
}
}
rr[x] = cnt;
}
int lca(int x, int y)
{
if (deep[x]>deep[y]) swap(x, y);
int del = deep[y] - deep[x];
for (int i=logs;i>=0;i--) if ((del>>i) &1) y = pre[y][i];
if (x==y) return x;
for (int i=logs;i>=0;i--) if (pre[x][i]!=pre[y][i]) x = pre[x][i], y = pre[y][i];
return pre[x][0];
}
//pro
void init()
{
cnt = 0;
for (int i=1;i<=n;i++)
{
G[i].clear(), deep[i] = far[i] = ll[i] = rr[i] = 0;
for (int j=0;j<=logs;j++) pre[i][j] = 0;
}
for (int i=1;i<n;i++)
{
scanf("%d%d%d", &a[i], &b[i], &wi[i]);
G[a[i]].push_back(b[i]); G[b[i]].push_back(a[i]);
}
}
void solve()
{
dfs(1, 0);
int db4;
for (int i=1;i<n;i++)
{
int x = a[i], y = b[i];
if (deep[x]>deep[y]) swap(x, y);
update(ll[y], wi[i]);
update(rr[y]+1, -wi[i]);
}
for (int i=1;i<=q;i++)
{
int opr; scanf("%d", &opr);
if (opr==0)
{
int u; scanf("%d", &u);
printf("%d\n", dist(Sc[s])+dist(Sc[u])-2*dist(Sc[lca(s, u)]));//一定要找s,u在DFS序列中的位置
s = u;
} else
{
int x, w; scanf("%d%d", &x, &w);
int y = (deep[a[x]]>deep[b[x]]) ? a[x] : b[x];
update(ll[y], w-wi[x]);
update(rr[y]+1, wi[x]-w);
wi[x] += w-wi[x];//边权点权分清楚
}
}
}
int main()
{
#ifndef ONLINE_JUDGE
freopen(FN2".in","r",stdin);
freopen(FN2".out","w",stdout);
#endif
while (scanf("%d%d%d", &n, &q, &s)==3)
{
init();
solve();
}
return 0;
}

树链剖分+线段树也可以。(此题卡vector,这迫使我试了一次链式前向星)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i, j, k) for (i=(j);i<=(k);i++)
#define fd(i, k, j) for (i=(k);i>=(j);i--)
#define rd(a) scanf("%d", &a)
#define rd2(a, b) scanf("%d%d", &a, &b)
#define rd3(a, b, c) scanf("%d%d%d", &a, &b, &c)
#define ms(i, j) memset(i, j, sizeof i)
#define FN2 "poj2763"
using namespace std;
const int MAXN = 100001 + 5;
struct data{int to, next;}e[MAXN*2];
int head[MAXN],cnt;
int n, q, s, ai[MAXN], bi[MAXN], wi[MAXN];
int dep[MAXN], fa[MAXN], son[MAXN], siz[MAXN], p[MAXN], top[MAXN], pre;
void ins(int u, int v) {cnt++, e[cnt].to = v, e[cnt].next = head[u],head[u] = cnt;}
void dfs1(int u, int p) {
int i;
dep[u] = dep[p] + 1, fa[u] = p, siz[u] = 1;
for (i=head[u];i!=-1;i=e[i].next) {
int v = e[i].to;
if (v!=p) {
dfs1(v, u);
siz[u] += siz[v];
if (son[u]==-1||siz[son[u]]<siz[v]) son[u] = v;
}
}
}
void dfs2(int u, int chain) {
int i;
top[u] = chain, p[u] = ++pre;
if (son[u]!=-1) {
dfs2(son[u], chain);
for (i=head[u];i!=-1;i=e[i].next) {
int v = e[i].to;
if (v!=fa[u]&&v!=son[u]) dfs2(v, v);
}
}
}
int sumv[MAXN*4];
void pushup(int o) {
int lc = o*2, rc = o*2+1;
sumv[o] = sumv[lc] + sumv[rc];
}
void update(int o, int l, int r, int x, int y, int v) {
int lc = o*2, rc = o*2+1, M = (l+r)/2;
if (x<=l&&r<=y) {
sumv[o] = v;
return ;
}
if (x<=M) update(lc, l, M, x, y, v);
if (M<y) update(rc, M+1, r, x, y, v);
pushup(o);
}
int query(int o, int l, int r, int x, int y) {
int lc = o*2, rc = o*2+1, M = (l+r)/2, ret = 0;
if (x<=l&&r<=y) {
return sumv[o];
}
if (x<=M) ret += query(lc, l, M, x, y);
if (M<y) ret += query(rc, M+1, r, x, y);
return ret;
}
int findSUM(int u, int v) {
int f1 = top[u], f2 = top[v], ret = 0;
while (f1!=f2) {
if (dep[f1]<dep[f2]) swap(f1, f2), swap(u, v);
ret += query(1,1,n,p[f1],p[u]);
u = fa[f1];
f1 = top[u];
}
if (dep[u]<dep[v]) swap(u, v);
return ret + query(1,1,n,p[v]+1,p[u]);
}
void init() {
int i; pre = 0;
fo (i, 1, n) dep[i] = fa[i] = siz[i] = p[i] = top[i] = 0, son[i] = -1, head[i] = e[i].next = -1;
fo (i, 1, n*4) sumv[i] = 0;
fo (i, 1, n-1) {
rd3(ai[i], bi[i], wi[i]);
ins(ai[i], bi[i]), ins(bi[i], ai[i]);
}
dfs1(1, 0), dfs2(1, 1);
fo (i, 1, n-1) {
int a1 = ai[i], b1 = bi[i];
if (dep[a1]>dep[b1]) update(1, 1, n, p[a1], p[a1], wi[i]);
if (dep[a1]<dep[b1]) update(1, 1, n, p[b1], p[b1], wi[i]);
}
}
void solve() {
int i;
fo (i, 1, q) {
int opt; rd(opt);
if (opt==0) {
int u; rd(u);
printf("%d\n", findSUM(u, s));
s = u;
} else {
int u, w; rd2(u, w);
int a1 = ai[u], b1 = bi[u];
if (dep[a1]>dep[b1]) update(1, 1, n, p[a1], p[a1], w);
if (dep[a1]<dep[b1]) update(1, 1, n, p[b1], p[b1], w);
}
}
}
int main() {
#ifndef ONLINE_JUDGE
freopen(FN2".in","r",stdin);freopen(FN2".out","w",stdout);
#endif
while (rd3(n, q, s)==3) init(), solve();
return 0;
}

------ 本文结束 ------