【嵊中模拟2019.02.01】【BZOJ1103】大都市meg
题解
设DFS序(栈操作序列)为$seq$,点$i$入栈位置为$u_{i}$,出栈位置为$v_{i}$。
发现以$i$为根的子树的所有点的出入栈位置都在$u_{i}$和$v_{i}$之间。
考虑设标记序列$mark$,令$\forall i\in [1,n], mark_{u_{i}}=1, mark_{v_{i}}=-1$,代表点$i$到父亲的这条边的影响,入栈时增加$1$,出站时增加$-1$
若点$i$到父亲的这条边从土路被改为公路,则要消去这条边的影响,即$mark_{u_{i}}-1, mark_{v_{i}}+1$。
用树状数组维护$mark$的前缀和序列,设为$sum$,则$sum_{i}$即为根到点$i$的路径上的边数,支持单点修改和区间查询。
代码
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<algorithm> using namespace std; int n; struct E{ int to,nxt; }e[500003]; int sum; int h[250003]; void adde(int from,int to){ sum++; e[sum].to=to; e[sum].nxt=h[from]; h[from]=sum; } bool vis[250003]; int seq[500003],cnt; int u[250003],v[250003]; void dfs(int x){ seq[++cnt]=x; u[x]=cnt; int y; for(int i=h[x];i;i=e[i].nxt){ y=e[i].to; if(vis[y]) continue; vis[y]=1; dfs(y); } seq[++cnt]=x; v[x]=cnt; } int t[500003]; int lowbit(int p){ return p&(-p); } void add(int p,int n,int x){ while(p<=n){ t[p]+=x; p+=lowbit(p); } } int fpre(int p){ int ans=0; while(p){ ans+=t[p]; p-=lowbit(p); } return ans; } int m; int max(int x,int y){ if(x>y) return x; return y; } int main(){ freopen("data.in","r",stdin); freopen("data.out","w",stdout); scanf("%d",&n); sum=0; memset(h,0,sizeof(h)); int x,y; for(int i=1;i<n;i++){ scanf("%d%d",&x,&y); adde(x,y); adde(y,x); } memset(vis,0,sizeof(vis)); cnt=0; vis[1]=1; dfs(1); memset(t,0,sizeof(t)); for(int i=2;i<=n;i++){ add(u[i],cnt,1); add(v[i],cnt,-1); } scanf("%d",&m); getchar(); char typ; int dlvcnt=0; for(;1;){ typ=getchar(); getchar(); scanf("%d",&x); getchar(); if(typ=='A'){ scanf("%d",&y); getchar(); x=max(x,y); add(u[x],cnt,-1); add(v[x],cnt,+1); } else { printf("%d\n",fpre(u[x])); dlvcnt++; if(dlvcnt==m) break; } } return 0; }