图的深搜+树状数组练习 POJ 3321(JAVA)

关于树状数组:参看:[url]http://128kj.iteye.com/blog/1743633[/url]
POJ3321 题意:
一棵具有n个节点的树,一开始,每个节点上都有一个苹果。现在给出m组动态的操作:
(C,i)是摘掉第i个节点上面的苹果(若苹果不存在,则为加上一个苹果),(Q,i)是查询以第i个节点为根的子树有几个苹果(包括第i个节点)。

输入是叉之间的关系,

1 2
1 3

就是主干上面两个叉分别是2 和3.
[img]http://dl.iteye.com/upload/attachment/0077/7432/7482a5a4-8a0d-34fb-bb92-abc3c6f2d1e1.gif[/img]

样例:
Sample Input

3
1 2
1 3
3
Q 1
C 2
Q 1

Sample Output

3
2



分析:
用树状数组求和,这个数组怎么来定?

从root开始dfs下去记录第一次访问u点时的时间戳为Begin[u](下标),当访问完以u为根的所有子树以后要向上回溯的时候,再记录一个时间戳End[u](下标),这样,这棵树上子树u上的所有苹果和就是Begin[u]到End[u]的区间和,操作就和普通的树状数组一样了~

下面是AC过的代码:

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
import java.util.*;

public class Main{
//把苹果树作图处理了。
private List<ArrayList<Integer>> G;// 邻接表。注:使用邻接矩阵内存溢出。
private int k;//顶点数目
private boolean[] visited;//判断顶点是否被访问过
private int[] Begin;
private int[] End;
private int Count=0;
private int Tree[];//树状数组


public Main(int k,List<ArrayList<Integer>> G){
this.k=k;
this.G=G;
visited = new boolean[k+1];
Begin=new int[k+1];
End=new int[k+1];
Tree=new int[k+1];
}

public int getBegin(int i){
return Begin[i];
}

private void cl(){
for(int i=0;i<=k;i++)
visited[i]=false;
}

private int getEnd(int i){
return End[i];
}

public boolean getVis(int i){
return visited[i];
}

private void setVis(int i,boolean fal){
visited[i]=fal;
}


private int lowbit(int i) {
return i&(-i);
}

void Update(int i,int Num){
while(i<= k){
Tree[i] += Num;
i += lowbit(i);
}
}

int Sum(int i){
int sum = 0;
while(i>0){
sum += Tree[i];
i -= lowbit(i);
}
return sum;
}
public static void main(String[] args) throws IOException{
StreamTokenizer sc = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
int k;
sc.nextToken();
k = (int) sc.nval; //顶点数

List<ArrayList<Integer>> G;
//构建邻接表
G = new ArrayList<ArrayList<Integer>>();
for(int i = 0;i<=k;i++)
G.add(new ArrayList<Integer>());//初始化邻接表
for (int i = 1; i <k; i++) {
sc.nextToken();
int u = (int) sc.nval;
sc.nextToken();
int v = (int) sc.nval;
if(!G.get(u).contains(v)) {//避免重边的情况比如a b可能出现两次的情况
G.get(u).add(v);

}
//对于无向图
if(!G.get(v).contains(u)) {
G.get(v).add(u);

}
}

Main ma=new Main(k,G);
ma.dfs(1);

for(int i = 1;i <= k;i++){
ma.Update(i,1);
}
sc.nextToken();
int M=(int) sc.nval;
ma.cl();

for(int i = 0;i < M;i++){
sc.nextToken();
String Op=sc.sval;
sc.nextToken();
int Num=(int) sc.nval;
if(Op.equals("C")){
if(!ma.getVis(Num))
ma.Update(ma.getBegin(Num),-1);//摘苹果
else
ma.Update(ma.getBegin(Num),1);//长一个
ma.setVis(Num,!ma.getVis(Num));
}else if(Op.equals("Q")){
System.out.printf("%d\n",ma.Sum(ma.getEnd(Num))-ma.Sum(ma.getBegin(Num)-1));
}
}
}




// 深搜
private void dfs(int v) {
Begin[v] = ++Count;
visited[v] = true;
//System.out.print(v+" ");

for (int i = 0; i <G.get(v).size(); i++) {
//递归调用搜索没有被访问过的当前节点的下一个节点(邻接点)
int k=G.get(v).get(i);
if (!visited[k])
dfs(k);
}
End[v] = Count;
}

}


源码:
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值