首页 > 代码库 > HDU4836 The Query on the Tree(树状数组&&LCA)

HDU4836 The Query on the Tree(树状数组&&LCA)

由于智力的问题,百度之星完全lu不动。。开场看第一题根据题目给的条件我觉得一定是可以构造出来的,题目给的意思颇有鸽巢原理的感觉,于是觉得开场第一题应该就是智力构造题了,想了半个小时,发现完全想不动,于是只能放弃了去想后面的题。

然后看第二题的数据结构,树上的询问,支持点修改,询问子树和,还有换根,然后心里想,我擦,这不是LCT么,但是我没学呀,然后细心的翻出之前打印的论文研读了很久,发现普通的LCT只能解决询问树路径上的东西,然后看论文上写如果支持子树操作的话就需要Euler-tour-tree什么的,想了一个多小时都想不到怎么用LCT,最后只能作罢。

然后看了三四题觉得也没什么思路,就去看第五题了,我感觉第五题是有思路的,对两个串建自动机,然后dp[i]定义为状态i的胜率,那么每个状态有两个转移边,转移到xi以及xj状态,那么dp[i]=1/2(dp[xi]+dp[xj]),然后边界就是我方胜利的状态为1,敌方胜利的状态为0,然后我希望能够通过发现图里深层的关系以期避免高斯消元,但是怎么搞都觉得是要高斯消元的,但是因为题目输出的是最简分数,分数版本的高斯消元我感觉我是写不粗来的,然后就只能作罢了。赛后听英姐说用LCM去消,我就明白了其实就是消元的时候按照高中学的那种消去法就好了,不要搞什么小数出来,有空写写看下能不能过。。

最后只能回头去看最多人过的第二题了,在纸上画了一下发现规律了,将原来的树保持不变,修改的时候按照传统的树状数组的点更段询就可以了,关键是在询问的时候,如果当前 询问的点是当前根的父亲,那么答案应该是 所有权值的和-(询问点包含根的那棵子树的和),否则就直接询问就可以了。要写一个LCA来求出询问点到根的路径的下一个点,然后每次询问都是logn的。

好久没写代码了呢,回头搜下看下第一题是怎么作粗来的。。

?
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
#pragma warning(disable:4996)
#include <iostream>
#include <cstring>
#include <string>
#include <vector>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
 
#define maxn 10050
#define maxlogv 16
int bit[maxn];
int n;
 
void add(int x, int v)
{
    while (x <= n){
        bit[x] += v;
        x += x&(-x);
    }
}
 
int query(int x)
{
    int ret = 0;
    while (x > 0){
        ret += bit[x];
        x -= x&(-x);
    }
    return ret;
}
 
 
int first[maxn];
int nxt[2 * maxn];
int vv[2 * maxn];
int e;
void addEdge(int u, int v)
{
    vv[e] = v; nxt[e] = first[u];
    first[u] = e++;
}
 
 
int pre[maxn];
int post[maxn];
vector<int> G[maxn];
int val[maxn];
int dfs_clock;
int p[maxn][maxlogv];
int dep[maxn];
 
 
void dfs(int u,int fa,int d)
{
    p[u][0] = fa; dep[u] = d;
    pre[u] = ++dfs_clock; int v;
    for (int i = first[u]; i !=-1; i=nxt[i]){
        v = vv[i];
        if (!pre[v]) dfs(v,u,d+1);
    }
    post[u] = dfs_clock;
}
 
void setRoot(int root)
{
    memset(pre, 0, sizeof(pre));
    memset(post, 0, sizeof(post));
    memset(p, -1, sizeof(p));
    memset(dep, 0, sizeof(dep));
    dfs_clock = 0;
    dfs(root,-1,0);
    memset(bit, 0, sizeof(bit));
    for (int i = 1; i <= n; i++){
        add(pre[i], val[i]);
    }
}
 
void getp()
{
    for (int j = 0; j + 1 <= maxlogv; j++){
        for (int i = 1; i <= n; i++){
            if (p[i][j] != -1){
                p[i][j + 1] = p[p[i][j]][j];
            }
        }
    }
}
 
bool isFather(int u, int v)
{
    return pre[u] <= pre[v] && pre[v] <= post[u];
}
 
int findPostFather(int u, int v)
{
    int gap = dep[v] - dep[u];
    gap -= 1;
    for (int i = maxlogv; i >= 0; i--){
        if ((gap >> i) & 1){
            v = p[v][i];
        }
    }
    return v;
}
 
int main()
{
    int T; cin >> T; int ca = 0;
    while (T--)
    {
        scanf("%d", &n);
        int ui, vi;
        memset(first, -1, sizeof(first)); e = 0;
        for (int i = 0; i < n - 1; i++){
            scanf("%d%d", &ui, &vi);
            addEdge(ui, vi);
            addEdge(vi, ui);
        }
        int tot = 0;
        for (int i = 1; i <= n; i++){
            scanf("%d", val + i);
            tot += val[i];
        }
        setRoot(1);
        int root = 1;
        getp();
        int m; scanf("%d", &m);
        char s[12];
        printf("Case #%d:\n", ++ca);
        for (int i = 0; i < m; i++){
            scanf("%s",s);
            if (s[0] == ‘Q‘){
                scanf("%d", &ui);
                if (ui == root) {
                    printf("%d\n", tot); continue;
                }
                if (isFather(ui, root)){
                    vi = findPostFather(ui,root);
                    printf("%d\n", tot - (query(post[vi]) - query(pre[vi] - 1)));
                }
                else{
                    printf("%d\n", query(post[ui]) - query(pre[ui] - 1));
                }
            }
            else if (s[0] == ‘C‘){
                scanf("%d%d", &ui, &vi);
                add(pre[ui], vi - val[ui]);
                tot += vi - val[ui];
                val[ui] = vi;
            }
            else{
                scanf("%d", &ui);
                root = ui;
            }
        }
    }
    return 0;
}