牛影传说【线段树+BFS序运用】

题目:

牛影村有N个据点,这些据点有N-1条双向道路连接,任意两个据点之间有且仅有一条路径相通。最初每个据点都驻扎着一些牛。神牛H具有法力无边的能力,他随时都可能会降临牛影村的某个据点,每来到一个据点,他可能使用如下三种法力中的一种:

  1、在据点i中选择一头强壮的牛,复制一个一模一样的。(无牛可以继续复制 即直接+1即可)

  2、把据点i及其所有与i有道路连接的据点中的所有牛都复制一分(这些据点的牛数量加倍)。

  3、把据点i及其所有与i有道路连接的据点中的所有牛都带到天宫屠杀

  你作为牛影村的首领,观察统计了神牛H降临牛影村的次数以及他每次施展的法术,现在神牛H再也不会来了,但你想知道神牛为牛影村创造了多少头牛。

Input

  第1行是一个整数N,表示牛影村中据点个数,据点分别编号为1..N。  第2行有N个非负整数,依次表示每个据点开始时牛的个数。  接下来的N-1行,每行有两个自然数A和B,表示据点A和据点B之间有一条道路连接。  接下来一个整数M,表示神牛使用法力的次数。  接下来M行,每行以1,2或3开头,代表神牛当前所施展的法力,接着一个自然数i,表示本次法力作用的的据点编号。

Output

  一个整数,表示神牛H创造的牛的数量,这个数可能很大,你需要输出它除以2009的余数即可。

Sample Input 1 

3
1 2 3
1 2
1 3
4
2 1
3 2
1 3
2 1

Sample Output 1

14


题解

   思考 法力2、3(法力1除了括号里那个坑人的地方 <<原题没有)

       很明显每一个法力能够修改的是不止一个村庄的 

       那么分析这些村庄的联系,  注意题中 牛影村道路 是成一棵树的。

       那么关于任意法力作用点:

            1. 有(唯一)作用点村庄

            2. 有唯一的父亲村庄

            3. 有一系列儿子村庄

        而关于1,2点很明显只能单点修改 (这并不是什么大问题)

       而对于第3点,考虑这一系列儿子村庄的连系

                儿子之间的连系大体存在两种:

                     1.BFS的连系  (节点 儿子具有连续性     即一个节点的所有儿子是排在一起的  但不一定儿子紧挨在父亲后面)

                     2.DFS的连系 (父亲与其儿具有连续性  即一个节点的所有子孙都紧挨在其后面)

                这道题我们

                      对于BFS 我们很容易能想到线段树去维护更改(连续序列的维护)

                      而对于DFS 蒟蒻我一筹莫展

        当建立好BFS序之后,简直就是线段树的模板题了

        (但这里因为维护量有两个,所以代码实现是有点容易出错的)

        

主要Code

        BFS序的建立

                涉及变量:

                        q 和 vis  实现树的Bfs遍历

                        Bfs_Time  实现Bfs序列

                        Re(i)    记录村庄 i 在Bfs序中的位置

                        L(i),R(i)   记录村庄 i 的儿子 在Bfs序中所对应连续序列的左右端点

                        Num(i)    记录Bfs序中第i号对应的村庄 

                        以及道路相关变量(全Code里包含相关声明)

void Bfs(){
	queueq;
	bool vis[Nn]={0};
	memset(Pa,0,sizeof(Pa));
	
	vis[1]=1;
	Num[1]=1;
	q.push(1);
	Re[1]=++Bfs_Time;
	while(!q.empty()){
		int t=q.front();q.pop();
		L[Pa[t]]=min(L[Pa[t]],Re[t]);
		R[Pa[t]]=max(R[Pa[t]],Re[t]);	//计算BFS序中L,R <--   先看下面初始化 
		
		for(int i=F[t];i;i=E[i].next){
			int c=E[i].to;
			if(vis[c]) continue ; vis[c]=1;
			Re[c]=++Bfs_Time; Num[Bfs_Time]=c;
			
			Pa[c]=t;
			q.push(c);
			L[t]=Re[c];
			R[t]=Re[c];   //初始化<--     再看上面计算     (Re[c]没有独特意义,只是为了规避干扰)
			
			continue ;
		}
		
		continue ;
	}
	
	return ;
}

            线段树相关   (见识某大佬的Code后整合了问题处理 <

int Np=0;
int Root;
struct data{
	int l,r;   
	ll id;	
}G[Nn];	   //线段树 节点G 

int Double[Nn];        //标记 数量加倍 (初始化为1,Build函数中初始化) 
int Delet[Nn]={0};	//标记 天宫杀牛 (初始化为0)

void PushData(int now){
	G[now].id=(G[G[now].l].id+G[G[now].r].id)%Mod;
	return ;
}

void Build(int &now,int i,int j){
	now=++Np;
	Double[now]=1;
	
	if(i+1==j){
		G[now].id=(ll)Cow[Num[i]];
		return ;
	}
	
	int mid=i+j>>1;
	Build(G[now].l,i,mid);
	Build(G[now].r,mid,j);
	
	PushData(now);
	return ;
}

void Down(int now){
	if(Delet[now]){
		Delet[now]=0;
		
		G[G[now].l].id=0;
		G[G[now].r].id=0;
		Delet[G[now].l]=1;
		Delet[G[now].r]=1;
		
		Double[now]=1;			//杀牛后翻倍无效,初始化Double 
		return ;
	}
	
	if(Double[now]==1) return ;
	
	G[G[now].l].id=(G[G[now].l].id*Double[now])%Mod;
	G[G[now].r].id=(G[G[now].r].id*Double[now])%Mod;
	Double[G[now].l]=(Double[G[now].l]*Double[now])%Mod;
	Double[G[now].r]=(Double[G[now].r]*Double[now])%Mod;
	
	Double[now]=1;
	return ;
}
ll Make(int now,int i,int j,int x,int y,int op){
	if(now>0)

	if(i>=x && j<=y){
		if(op==1){
			G[now].id++;
			return 1;
		}
		
		if(op==2){
			ll ok=G[now].id;
			G[now].id=(G[now].id*2)%Mod;
			Double[now]=(Double[now]*2)%Mod;
			return ok%Mod;
		}
		
		if(op==3){
			G[now].id=0;
			Delet[now]=1;
			return 0;
		}
	}
	
	int mid=i+j>>1;
	Down(now);
	ll ok=0;
	
	if(mid>=y) ok=Make(G[now].l,i,mid,x,y,op);
	else if(mid<=x) ok=Make(G[now].r,mid,j,x,y,op);
	else ok=Make(G[now].l,i,mid,x,y,op)+Make(G[now].r,mid,j,x,y,op);
	
	PushData(now);
	return ok%Mod;
}


全Code

            

#include
#include
#include
#include
#include
using namespace std;

const int Mod=2009; 
const int Nn=200005;
typedef long long ll; 

void Read(int &x){
	x=0;
	char c=getchar();
	while(!isdigit(c)) c=getchar();
	while(isdigit(c)) x=x*10-'0'+c,c=getchar();
	
	return ; 
}
void Out(ll x){
	int num=0;
	char s[5005];
	while(x) s[++num]=x%10,x/=10;
	for(int i=num;i>=1;i--) putchar(s[i]+'0');
	putchar('\n');
	
	return ;
}

int N,M;
int Cow[Nn];
int Tot=0;
int F[Nn]={0};
struct alpha{
	int from,to,next;   //储存道路 
}E[Nn];

void Addl(int x,int y){
	E[++Tot]=(alpha){x,y,F[x]};
	F[x]=Tot;
	return ;
}

void Init(){
	Read(N);
	for(int i=1;i<=N;i++)
	 Read(Cow[i]);
	for(int i=1;iq;
	bool vis[Nn]={0};
	memset(Pa,0,sizeof(Pa));
	
	vis[1]=1;
	Num[1]=1;
	q.push(1);
	Re[1]=++Bfs_Time;
	while(!q.empty()){
		int t=q.front();q.pop();
		L[Pa[t]]=min(L[Pa[t]],Re[t]);
		R[Pa[t]]=max(R[Pa[t]],Re[t]);	//计算BFS序中L,R <--   先看下面初始化 
		
		for(int i=F[t];i;i=E[i].next){
			int c=E[i].to;
			if(vis[c]) continue ; vis[c]=1;
			Re[c]=++Bfs_Time; Num[Bfs_Time]=c;
			
			Pa[c]=t;
			q.push(c);
			L[t]=Re[c];
			R[t]=Re[c];   //初始化<--     再看上面计算     (Re[c]没有独特意义,只是为了规避干扰)
			
			continue ;
		}
		
		continue ;
	}
	
	return ;
}

int Np=0;
int Root;
struct data{
	int l,r;   
	ll id;	
}G[Nn];	   //线段树 节点G 

int Double[Nn];        //标记 数量加倍 (初始化为1,Build函数中初始化) 
int Delet[Nn]={0};	//标记 天宫杀牛 (初始化为0)

void PushData(int now){
	G[now].id=(G[G[now].l].id+G[G[now].r].id)%Mod;
	return ;
}

void Build(int &now,int i,int j){
	now=++Np;
	Double[now]=1;
	
	if(i+1==j){
		G[now].id=(ll)Cow[Num[i]];
		return ;
	}
	
	int mid=i+j>>1;
	Build(G[now].l,i,mid);
	Build(G[now].r,mid,j);
	
	PushData(now);
	return ;
}

void Down(int now){
	if(Delet[now]){
		Delet[now]=0;
		
		G[G[now].l].id=0;
		G[G[now].r].id=0;
		Delet[G[now].l]=1;
		Delet[G[now].r]=1;
		
		Double[now]=1;			//杀牛后翻倍无效,初始化Double 
		return ;
	}
	
	if(Double[now]==1) return ;
	
	G[G[now].l].id=(G[G[now].l].id*Double[now])%Mod;
	G[G[now].r].id=(G[G[now].r].id*Double[now])%Mod;
	Double[G[now].l]=(Double[G[now].l]*Double[now])%Mod;
	Double[G[now].r]=(Double[G[now].r]*Double[now])%Mod;
	
	Double[now]=1;
	return ;
}
ll Make(int now,int i,int j,int x,int y,int op){
	if(now>0)

	if(i>=x && j<=y){
		if(op==1){
			G[now].id++;
			return 1;
		}
		
		if(op==2){
			ll ok=G[now].id;
			G[now].id=(G[now].id*2)%Mod;
			Double[now]=(Double[now]*2)%Mod;
			return ok%Mod;
		}
		
		if(op==3){
			G[now].id=0;
			Delet[now]=1;
			return 0;
		}
	}
	
	int mid=i+j>>1;
	Down(now);
	ll ok=0;
	
	if(mid>=y) ok=Make(G[now].l,i,mid,x,y,op);
	else if(mid<=x) ok=Make(G[now].r,mid,j,x,y,op);
	else ok=Make(G[now].l,i,mid,x,y,op)+Make(G[now].r,mid,j,x,y,op);
	
	PushData(now);
	return ok%Mod;
}

ll Ans=0;
void Solve(){
	Bfs();
	Read(M);
	Build(Root,1,N+1);
	
	for(int i=1;i<=M;i++){
		int x,y;
		Read(x),Read(y);
		Ans=(Ans+Make(Root,1,N+1,Re[y],Re[y]+1,x))%Mod;
	
		if(x!=1){
			if(Pa[y]) 
			 Ans=(Ans+Make(Root,1,N+1,Re[Pa[y]],Re[Pa[y]]+1,x))%Mod;
			if(L[y])
			 Ans=(Ans+Make(Root,1,N+1,L[y],R[y]+1,x))%Mod;
		}
	}
	
	Out(Ans);
	return ;
}
int main(){
	Init();
	Solve();
	return 0;
}

你可能感兴趣的:(线段树,Bfs序)