BTREE——动态点分治+虚树+可持久化线段树+容斥
题目大意:给出一棵$n$个点的树及$Q$次询问,每次询问给出$k$个关键点及他们的控制距离,求有多少点被控制。
对于每次询问,我们对给出的点建虚树并求出虚树上每个点的最远控制距离(从上往下&从下往上两遍$DP$即可求出)。我们将答案的贡献分为两部分:虚树上每个点的贡献及虚边上每个点的贡献。对于虚树上每个点的贡献,假设一个点$x$的控制距离为$v$,那么只需要求出$x$子树中与他距离$\le v$的点的个数再减掉他在虚树上的子节点在原树上所属子树中与他距离$\le v$的点即可。对于虚边上的贡献,我们需要找到一个中间点$z$使得虚边两端点在他子树中的控制深度相等,那么$z$上方的虚边归父节点$a$控制,$z$及其下方的点归子节点$b$控制。对于父节点$a$控制部分,我们只需要用$a$下方那个点的子树中被$a$控制的点数减掉$z$子树中被$a$控制的点数即可。对于子节点$b$控制部分,我们先求出整棵树中被$b$控制的点数,再减掉$b$子树中被$b$控制的点数即可得到$b$上方被$b$控制的点数。但这些点中可能还有在$z$上方的点是不合法的,假设$b$控制距离为$w$,$dis(b,z)=d$,那么只要求出与$z$距离$\le w-d$的点的个数减掉$z$子树中与$z$距离$\le w-d$的点的个数即可求出在$z$上方的不合法点个数,将这部分减掉即可。
求一个点子树中与它距离小于等于一个值的点数可以按深度建主席树然后查找对应深度主席树中区间和即可;求与一个点距离小于等于一个值的点数可以用动态点分治求解。
#include<set> #include<map> #include<queue> #include<stack> #include<cmath> #include<vector> #include<bitset> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; int n,Q; int x,y,k; int tot; int dfn; int a[50010]; int head[50010]; int to[100010]; int s[50010]; int next[100010]; int f[50010][17]; int dep[50010]; int st[50010]; int top; int dis[50010]; int vis[50010]; int val[50010]; int ans; vector<int>v[50010]; inline void add(int x,int y) { next[++tot]=head[x]; head[x]=tot; to[tot]=y; } namespace persistent_segment_tree { int mx; int dfn; int cnt; int a[50010]; int d[50010]; int s[50010]; int t[50010]; int q[50010]; int rt[50010]; int ls[1000010]; int rs[1000010]; int sum[1000010]; inline bool cmp(int x,int y) { return d[x]<d[y]; } inline void dfs(int x,int fa) { s[x]=++dfn; q[dfn]=x; d[x]=d[fa]+1; for(int i=head[x];i;i=next[i]) { if(to[i]!=fa) { dfs(to[i],x); } } t[x]=dfn; } inline void insert(int &rt,int pre,int l,int r,int k) { rt=++cnt; sum[rt]=sum[pre]+1; ls[rt]=ls[pre]; rs[rt]=rs[pre]; if(l==r) { return ; } int mid=(l+r)>>1; if(k<=mid) { insert(ls[rt],ls[pre],l,mid,k); } else { insert(rs[rt],rs[pre],mid+1,r,k); } } inline int query(int rt,int l,int r,int L,int R) { if(!rt) { return 0; } if(L<=l&&r<=R) { return sum[rt]; } int mid=(l+r)>>1; int res=0; if(L<=mid) { res+=query(ls[rt],l,mid,L,R); } if(R>mid) { res+=query(rs[rt],mid+1,r,L,R); } return res; } inline void work() { for(int i=1;i<=n;i++) { a[i]=i; } dfs(1,0); sort(a+1,a+1+n,cmp); for(int i=1;i<=n;i++) { if(d[a[i]]!=d[a[i-1]]) { insert(rt[d[a[i]]],rt[d[a[i]]-1],1,n,s[a[i]]); } else { insert(rt[d[a[i]]],rt[d[a[i]]],1,n,s[a[i]]); } mx=max(mx,d[a[i]]); } } inline int query_dep(int x,int k) { if(k<0)return 0; return query(rt[min(d[x]+k,mx)],1,n,s[x],t[x]); } }; namespace dynamic_point_partition { int dfn; int num; int cnt; int root; int s[50010]; int f[50010]; int mx[50010]; int lg[100010]; int dep[50010]; int vis[50010]; int size[50010]; int g[100010][18]; vector<int>rt[50010]; vector<int>frt[50010]; inline void dfs(int x,int fa) { g[++dfn][0]=dep[x]; s[x]=dfn; for(int i=head[x];i;i=next[i]) { if(to[i]!=fa) { dep[to[i]]=dep[x]+1; dfs(to[i],x); g[++dfn][0]=dep[x]; } } } inline void getroot(int x,int fa) { size[x]=1; mx[x]=0; for(int i=head[x];i;i=next[i]) { if(!vis[to[i]]&&to[i]!=fa) { getroot(to[i],x); size[x]+=size[to[i]]; mx[x]=max(mx[x],size[to[i]]); } } mx[x]=max(mx[x],num-size[x]); if(mx[x]<mx[root]) { root=x; } } inline int lca(int x,int y) { x=s[x],y=s[y]; if(x>y) { swap(x,y); } int len=lg[y-x+1]; return min(g[x][len],g[y-(1<<len)+1][len]); } inline int dis(int x,int y) { return dep[x]+dep[y]-(lca(x,y)<<1); } inline void partation(int x) { vis[x]=1; for(int i=head[x];i;i=next[i]) { if(!vis[to[i]]) { num=size[to[i]]; root=0; getroot(to[i],0); f[root]=x; partation(root); } } } inline void work() { dfs(1,0); for(int i=2;i<=dfn;i++) { lg[i]=lg[i>>1]+1; } for(int j=1;j<=17;j++) { for(int i=1;i<=dfn;i++) { if(i+(1<<j)-1>dfn) { break; } g[i][j]=min(g[i][j-1],g[i+(1<<(j-1))][j-1]); } } mx[0]=1<<30; num=n; root=0; getroot(1,0); partation(root); for(int x=1;x<=n;x++) { for(int i=x;i;i=f[i]) { rt[i].push_back(dis(x,i)); } for(int i=x;f[i];i=f[i]) { frt[i].push_back(dis(x,f[i])); } } for(int i=1;i<=n;i++) { sort(rt[i].begin(),rt[i].end()); sort(frt[i].begin(),frt[i].end()); } } inline int query_dis(int x,int k) { if(k<0)return 0; int res=0; for(int i=x;i;i=f[i]) { res+=upper_bound(rt[i].begin(),rt[i].end(),k-dis(x,i))-rt[i].begin(); } for(int i=x;f[i];i=f[i]) { res-=upper_bound(frt[i].begin(),frt[i].end(),k-dis(x,f[i]))-frt[i].begin(); } return res; } }; inline void dfs(int x) { s[x]=++dfn; dep[x]=dep[f[x][0]]+1; for(int i=1;i<=16;i++) { f[x][i]=f[f[x][i-1]][i-1]; } for(int i=head[x];i;i=next[i]) { if(to[i]!=f[x][0]) { f[to[i]][0]=x; dfs(to[i]); } } } inline bool cmp(int x,int y) { return s[x]<s[y]; } inline int lca(int x,int y) { if(dep[x]<dep[y]) { swap(x,y); } int d=dep[x]-dep[y]; for(int i=0;i<=16;i++) { if(d&(1<<i)) { x=f[x][i]; } } if(x==y) { return x; } for(int i=16;i>=0;i--) { if(f[x][i]!=f[y][i]) { x=f[x][i]; y=f[y][i]; } } return f[x][0]; } inline void insert(int x) { int anc=lca(x,st[top]); while(top>1&&dep[st[top-1]]>=dep[anc]) { v[st[top-1]].push_back(st[top]); top--; } if(anc!=st[top]) { v[anc].push_back(st[top]); st[top]=anc; } st[++top]=x; } inline int find_dep(int x,int y) { return abs(dep[x]-dep[y]); } inline void tree_dp1(int x) { dis[x]=-1<<30; if(vis[x]) { dis[x]=val[x]; vis[x]=0; } int len=v[x].size(); for(int i=0;i<len;i++) { int to=v[x][i]; tree_dp1(to); if(dis[x]<dis[to]-find_dep(x,to)) { dis[x]=dis[to]-find_dep(x,to); } } } inline void tree_dp2(int x,int fa) { if(dis[x]<dis[fa]-find_dep(fa,x)) { dis[x]=dis[fa]-find_dep(fa,x); } int len=v[x].size(); for(int i=0;i<len;i++) { int to=v[x][i]; tree_dp2(to,x); } } inline int ST(int x,int k) { for(int i=16;i>=0;i--) { if(dep[f[x][i]]>=k) { x=f[x][i]; } } return x; } inline void solve(int x) { ans+=persistent_segment_tree::query_dep(x,dis[x]); int sz=v[x].size(); for(int i=0;i<sz;i++) { int to=v[x][i]; int value=find_dep(x,to); int top=ST(to,dep[x]+1); ans-=persistent_segment_tree::query_dep(top,dis[x]-1); if(dis[x]+dis[to]<value) { ans+=persistent_segment_tree::query_dep(top,dis[x]-1); ans+=dynamic_point_partition::query_dis(to,dis[to]);; ans-=persistent_segment_tree::query_dep(to,dis[to]); } else { int len=(dis[x]-dis[to]+dep[x]+dep[to]+1)/2; int mid=ST(to,len); if(mid==x) { ans+=dynamic_point_partition::query_dis(to,dis[to]); ans-=persistent_segment_tree::query_dep(to,dis[to]); ans-=dynamic_point_partition::query_dis(top,dis[to]-value+1); ans+=persistent_segment_tree::query_dep(top,dis[to]-value+1); } else if(mid==to) { ans+=persistent_segment_tree::query_dep(top,dis[x]-1); ans-=persistent_segment_tree::query_dep(to,dis[x]-value); } else { ans+=persistent_segment_tree::query_dep(top,dis[x]-1); ans-=persistent_segment_tree::query_dep(mid,dis[x]-find_dep(x,mid)); ans+=dynamic_point_partition::query_dis(to,dis[to]); ans-=persistent_segment_tree::query_dep(to,dis[to]); ans-=dynamic_point_partition::query_dis(mid,dis[to]-find_dep(to,mid)); ans+=persistent_segment_tree::query_dep(mid,dis[to]-find_dep(to,mid)); } } solve(to); } val[x]=0; v[x].clear(); } int main() { scanf("%d",&n); for(int i=1;i<n;i++) { scanf("%d%d",&x,&y); add(x,y); add(y,x); } persistent_segment_tree::work(); dynamic_point_partition::work(); dfs(1); scanf("%d",&Q); while(Q--) { scanf("%d",&k); ans=0; for(int i=1;i<=k;i++) { scanf("%d",&a[i]); scanf("%d",&val[a[i]]); vis[a[i]]=1; } sort(a+1,a+1+k,cmp); top=0; if(a[1]!=1) { st[++top]=1; } for(int i=1;i<=k;i++) { insert(a[i]); } while(top>1) { v[st[top-1]].push_back(st[top]); top--; } tree_dp1(1); tree_dp2(1,0); solve(1); printf("%d\n",ans); } }