P8205 [Ynoi2005] vti gxy001

当树是条链时,询问即为区间顺序对数,所以这道题肯定是根号题。

我们考虑把这个问题转化为树上链查询,建出询问点集的虚树 $T$,设虚树根为 $rt$,答案即为 $\sum\limits_{u\in leaf(T)}f(rt,u)-\sum\limits_{u\in T\land u\notin leaf(T)}(s_u-1) f(rt,u)$,其中 $leaf(T)$ 表示虚树的所有叶子,$s_u$ 表示 $u$ 的儿子数,$f(a,b)$ 表示 $a$ 到 $b$ 这条链的答案,这样拆分后总询问数不会超过 $\sum (2t_i-1)$,且每个询问内两点都是祖先关系

下文中的 $m$ 均指拆分后的询问数。

由于询问数较多,所以我们考虑 $O(n\sqrt m)$ 的莫队算法,显然是要莫队二离的。先树分块,分块大小 $B=O(\frac n{\sqrt m})$,最好用 Top Cluster。

先考虑不在同一块内的询问,我们把询问按照上端点所在的块分类,把上端点在同一块内的询问按照下端点排序,直接在树上跑莫队。发现移动的时候的贡献是一个上下的路径中有多少个权值大于/小于 $a_i$,按照莫队二离的套路进行差分后要求的就是根到 $j$ 的路径上有多少个权值大于/小于 $a_i$,和普通莫队二离一样扫描线,就是 dfs 一遍,顺便用分块维护根到当前点路径上的边的权值。

对于在同一块内的询问,我们发现路径长度是 $O(B)$ 级别的,直接在上面说的 dfs 的时候顺便求出答案就好。

#include<iostream>
#include<vector>
#include<utility>
#include<tuple>
#include<algorithm>
#include<cmath>
using std::cin;
using std::cout;
std::vector<int> v[100010];
int n,a[100010],fa[100010],dfn[100010],rk[100010],dep[100010],dfx;
void dfs(int x){
	dfn[rk[x]=++dfx]=fa[x],dep[x]=dep[fa[x]]+1;
	for(auto u:v[x]) dfs(u);
}
namespace ST{
	int mx[17][100010],lg[100010];
	inline bool cmp(int x,int y){return dep[x]<dep[y];}
	void init(){
		for(int i=2;i<=n;i++) lg[i]=lg[i>>1]+1;
		for(int i=1;i<=n;i++) mx[0][i]=dfn[i];
		for(int i=1;i<17;i++)
			for(int j=1;j+(1<<i)-1<=n;j++)
				mx[i][j]=std::min(mx[i-1][j],mx[i-1][j+(1<<(i-1))],cmp);
	}
	inline int query(int l,int r){
		int k=lg[r-l+1];
		return std::min(mx[k][l],mx[k][r-(1<<k)+1],cmp);
	}
}
inline int lca(int x,int y){
	if(x==y) return x;
	if(rk[x]>rk[y]) std::swap(x,y);
	return ST::query(rk[x]+1,rk[y]);
}
long long ans[1000010];
namespace work{
	int B,stk[100010],tp,c[100010],bel[100010],R[100010];
	long long f1[100010],f2[100010],ans[2000010];
	std::vector<std::tuple<int,int,int,int>> vec,p1[100010],p2[100010];
	struct node{
		int a,b,id;
		node(int x,int y,int z):a(x),b(y),id(z){}
	};
	std::vector<node> q[100010];
	inline void update(int i,int v){
		for(;i<=n;i+=i&-i) c[i]+=v;
	}
	inline int query(int i){
		int ans=0;
		for(;i;i&=i-1) ans+=c[i];
		return ans;
	}	
	void dv(int x){
		int k=tp,ct=0;
		f1[x]=query(a[x]-1)+f1[fa[x]];
		f2[x]=query(n)-query(a[x])+f2[fa[x]];
		update(a[x],1);
		for(auto u:v[x]){
			dv(u);
			stk[++tp]=u;
			if(bel[u]) ++ct,bel[x]=bel[u];
		}
		update(a[x],-1);
		if(x==1||ct>1||tp-k>=B) bel[x]=x,tp=k;
	}
	int s1[320],s2[100010];
	inline void upd(int x,int v){
		for(int i=bel[x];i<=bel[n];i++) s1[i]+=v;
		for(int i=R[bel[x]];i>=x;i--) s2[i]+=v;
	}
	inline int que(int x){return s1[bel[x]-1]+s2[x];}
	void solve(int x){
		upd(a[x],1);
		for(auto [d,b,id,v]:p1[x]){
			long long u=0;
			while(b!=d) u+=que(a[b]-1),b=fa[b];
			ans[id]+=v*u;
		}
		for(auto [d,b,id,v]:p2[x]){
			long long u=0;
			while(b!=d) u+=s1[bel[n]]-que(a[b]),b=fa[b];
			ans[id]+=v*u;
		}
		for(auto u:v[x]) solve(u);
		upd(a[x],-1);
	}
	void work(){
		B=sqrt(6)*n/sqrt(vec.size());
		if(!B) B=1;
		dv(1);
		for(int i=vec.size()-1;~i;i--){
			auto [a,b,c,d]=vec[i];
			if(!bel[a]||dep[b]<=dep[bel[a]]) p1[a].emplace_back(a,b,i,-1),ans[i]+=f1[b]-f1[a];
			else q[bel[a]].emplace_back(a,b,i);
		}
		for(int i=1;i<=n;i++)if(i==bel[i]){
			std::sort(q[i].begin(),q[i].end(),[](node x,node y){return rk[x.b]<rk[y.b];});
			int l=i,r=i;
			for(auto [a,b,id]:q[i]){
				if(r!=b){
					int u=lca(r,b);
					if(r!=u) ans[id]-=f1[r]-f1[u],p1[l].emplace_back(u,r,id,1);
					if(b!=u) ans[id]+=f1[b]-f1[u],p1[l].emplace_back(u,b,id,-1);
					r=b;
				}
				if(dep[l]>dep[a]) ans[id]-=f2[l]-f2[a],p2[r].emplace_back(a,l,id,1);
				if(dep[l]<dep[a]) ans[id]+=f2[a]-f2[l],p2[r].emplace_back(l,a,id,-1);
				l=a;
			}
		}
		B=sqrt(n);
		for(int i=1;i<=n;i++) bel[i]=(i-1)/B+1,R[bel[i]]=i;
		solve(1);
		for(int i=1;i<=n;i++)
			for(int j=1,k=q[i].size();j<k;j++) ans[q[i][j].id]+=ans[q[i][j-1].id];
		for(int i=vec.size()-1;~i;i--){
			auto [a,b,c,d]=vec[i];
			::ans[c]+=d*ans[i];
		}
	}
}
int main(){
	std::ios::sync_with_stdio(false),cin.tie(nullptr);
	cin>>n,a[1]=1;
	for(int i=2;i<=n;i++) cin>>fa[i]>>a[i],v[fa[i]].push_back(i);
	dfs(1);
	ST::init();
	int m;
	cin>>m;
	for(int k=1;k<=m;k++){
		static int cnt[100010];
		int c;
		cin>>c;
		std::vector<int> v;
		for(int i=1,x;i<=c;i++) cin>>x,v.push_back(x);
		std::sort(v.begin(),v.end(),[](int x,int y){return rk[x]<rk[y];});
		for(int i=v.size()-1;i;i--) v.push_back(lca(v[i],v[i-1]));
		std::sort(v.begin(),v.end(),[](int x,int y){return rk[x]<rk[y];});
		v.erase(std::unique(v.begin(),v.end()),v.end());
		int u=v.front();
		for(auto x:v) cnt[x]=0;
		for(int i=v.size()-1;i;i--){
			if(!cnt[v[i]]) work::vec.emplace_back(u,v[i],k,1);
			else if(cnt[v[i]]>1) work::vec.emplace_back(u,v[i],k,-cnt[v[i]]+1);
			++cnt[lca(v[i],v[i-1])];
		}
	}
	work::work();
	for(int i=1;i<=m;i++) cout<<ans[i]<<'\n';
	return 0;
}