牛影传说【线段树+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(){ queue<int>q; 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后整合了问题处理 <<Make函数)
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<queue> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> 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;i<N;i++){ int x,y; Read(x),Read(y); Addl(x,y); Addl(y,x); } return ; } ll Sum=0; int Bfs_Time=0; int L[Nn],R[Nn],Re[Nn],Pa[Nn],Num[Nn]; void Bfs(){ queue<int>q; 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; }