题目:http://acm.hdu.edu.cn/showproblem.php?pid=4836
下午百度之星复赛里最简单的一题,虽然我还是1个小时才AC的,呃,下午果断被虐粗翔。
本题磨了1个小时才过,第1题还是很猥琐地用了随机数过的(真不知道怎么做)。
回到这题来,其实也不知道大牛们怎么做的,我只能用线段树+LCA搞了。
首先考虑根不改变的情况,那么我们可以将每个结点映射到线段树上去,让每个点对应的子树的点都落在某个连续区间上;
具体是在dfs的时候,用id来表示当前已经映射到线段树的编号,初始为0,进入某个结点X,令left[X]=right[X]=++id,然后继续dfs,对于X每个子节点j,dfs完了顺便更新right[X]=max(right[X], right[j]),这样在所有子节点遍历完的情况下,left[X]到right[X]就刚好是以X为根的子树的区间。在纸上模拟下就明白了。
然后对于查询和点更新就是个经典的线段树问题了。
比较麻烦的是根被改变的情况,假设我们现在要查询以X为根的子树的和,分析下当前根是root时与原先的不同。如果root原来就是X的祖先了,那么答案跟原来是一样的,再往下想其实就是只要root需要经过X原先某个祖先到达X的话,答案应该都一样的,所以这个好办。
而如果X刚好就是root,那么答案就是整棵树的和,也好处理。
剩下的就是原来X是root的祖先,我们假设X经过它的某个子节点Y到达root,那么很明显当前X的子树和,应该是整棵树的值减去原来Y对应的子树的和。因为X-Y这条边是root进行dfs到达X的最后一条边,换句话说这条边就把是不是X的子树的点分隔开了。
分析到这里问题就好办了,对于查询X,求出它们的最近公共祖先V,再进行判断即可。
#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
using namespace std;
#define lson o<<1
#define rson (o<<1)|1
#define N 10001
#define pb push_back
vector<int> V[N];
int t, ct, n;
int left[N], right[N], val[N], v2[N];
int s[N<<2], l[N<<2], r[N<<2];
int id, root;
bool f[N];
int depth[N], parent[16][N];
void dfs(int x){
left[x] = right[x] = ++id;
v2[id] = val[x];
for(int i=0; i<V[x].size(); i++){
int j=V[x][i];
if(f[j]) continue;
f[j]=1;
depth[j] = depth[x]+1;
parent[0][j] = x;
dfs(j);
right[x] = max(right[x], right[j]);
}
}
void maintain(int o){
s[o] = s[lson]+s[rson];
}
void build(int o, int ll, int rr){
l[o]=ll; r[o]=rr;
s[o]=0;
if(ll<rr){
int m = (ll+rr)>>1;
build(lson, ll, m);
build(rson, m+1, rr);
maintain(o);
}
else{
s[o] = v2[ll];
}
}
void update(int o, int p, int v){
if(l[o]==p && r[o]==p){
s[o]=v;
return;
}
int m = (l[o]+r[o])>>1;
if(p<=m) update(lson, p, v);
else update(rson, p, v);
maintain(o);
}
void init_lca(){
for(int k=0; k<15; k++){
for(int v=1; v<=n; v++){
if(parent[k][v]<0) parent[k+1][v]=-1;
else parent[k+1][v] = parent[k][parent[k][v]];
}
}
}
int lca(int u, int v){
if(depth[u]>depth[v]){
swap(u,v);
}
for(int k=0; k<16; k++){
if((depth[v]-depth[u])>>k & 1){
v = parent[k][v];
}
}
if(u==v) return u;
for(int k=15; k>=0; k--){
if(parent[k][u]!=parent[k][v]){
u=parent[k][u];
v=parent[k][v];
}
}
return parent[0][u];
}
int query(int o, int ll, int rr){
if(l[o]==ll && r[o]==rr) return s[o];
int m = (l[o]+r[o])>>1;
if(rr<=m) return query(lson, ll, rr);
else if(ll>m) return query(rson, ll, rr);
else return query(lson, ll, m)+query(rson, m+1, rr);
}
int query(int x){
if(x==root) return s[1];
int v = lca(root, x);
if(v!=x){
return query(1, left[x], right[x]);
}
for(int i=0; i<V[x].size(); i++){
int j=V[x][i];
if(depth[j]<depth[x]) continue;
v = lca(root, j);
if(v==j){//找到前面所说的Y
return s[1] - query(1, left[j], right[j]);
}
}
return 0;
}
inline void in(int &x){
char c=getchar();
x=0;
while(c<48 || c>57) c=getchar();
while(c>=48 && c<=57){
x = x*10+c-48;
c = getchar();
}
}
int main(){
in(t);
for(ct=1; ct<=t; ct++){
printf("Case #%d:\n", ct);
in(n);
for(int i=1; i<=n; i++) V[i].clear();
int x, y;
for(int i=1; i<n; i++){
in(x); in(y);
V[x].pb(y);
V[y].pb(x);
}
for(int i=1; i<=n; i++){
in(val[i]);
}
memset(f,0,sizeof(f));
memset(parent,-1,sizeof(parent));
f[1]=1;
id = 0;
depth[1]=1;
dfs(1);
init_lca();
build(1, 1, n);
int q;
char op[10];
in(q);
root=1;
while(q--){
scanf("%s", op);
in(x);
if(op[0]=='Q'){
printf("%d\n", query(x));
}
else if(op[0]=='C'){
in(y);
update(1, left[x], y);
}
else{
root = x;
}
}
}
return 0;
}