P3242 [HNOI2015]接水果 树上莫队+分块

题意:

有一颗大小为\(n\)的树,给定\(m\)条固定路径,再给定\(q\)条查询路径,求每条查询路径上第\(k\)大的固定路径

范围&性质:\(1\le n,m,q\le 4\times10^4\)

分析:

我们将问题拆成两部分,求每条询问路径上有几条固定路径,求一个固定路径集合中第\(k\)大的路径

我们发现前一部分可以用树上莫队解决,后一部分可以用分块解决,好像这道题就能做了

QED.

代码:

#include<bits/stdc++.h>

using namespace std;

namespace zzc
{
	typedef long long ll;
	const int maxn = 2e6+5;
	ll n,m,q,cnt=0,idx=0,l,r,sz,num,block,size;
	ll head[maxn],sum[maxn],line[maxn],bel[maxn],gs[maxn],fa[maxn][25],in[maxn],id[maxn],out[maxn];
	ll a[maxn],b[maxn],dep[maxn],L[maxn],R[maxn],ans[maxn];
	bool vis[maxn];
	vector<ll> v[maxn];
	
	struct node
	{
		ll l,r,id,k,lca;
	}Q[maxn];
	
	bool cmp1(node a,node b)
	{
		return (a.l/block)^(b.l/block)?(a.l<b.l):((a.l/block)&1)?(a.r<b.r):(a.r>b.r);
	}
	
	struct edge
	{
		ll to,nxt;
	}e[maxn<<1];
	
	void add(ll u,ll v)
	{
		e[++cnt].to=v;
		e[cnt].nxt=head[u];
		head[u]=cnt;
	}
	
	void dfs(ll u,ll ff)
	{
		fa[u][0]=ff;
		dep[u]=dep[ff]+1;
		in[u]=++idx;
		id[idx]=u;
		for(ll i=head[u];i;i=e[i].nxt)
		{
			ll v=e[i].to;
			if(v==ff) continue;
			dfs(v,u);
		}
		out[u]=++idx;
		id[idx]=u;
	}
	
	ll lca(ll x,ll y)
	{
		if(dep[x]<dep[y]) swap(x,y);
		for(ll i=20;i>=0;i--)
		{
			if(dep[fa[x][i]]>=dep[y])
			{
				x=fa[x][i];
			}
		}
		if(x==y) return x;
		for(ll i=20;i>=0;i--)
		{
			if(fa[x][i]!=fa[y][i])
			{
				x=fa[x][i];
				y=fa[y][i];
			}
		}
		return fa[x][0];
	}
	
	void Insert(ll x)
	{
		gs[x]++;
		sum[bel[x]]++;
	}
	
	void Delete(ll x)
	{
		gs[x]--;
		sum[bel[x]]--;
	}
	
	void add(ll pos)
	{
		for(ll i=0;i<(ll)v[pos].size();i++)
		{
			line[v[pos][i]]++;
			if(line[v[pos][i]]==2) Insert(a[v[pos][i]]);
		}
	}
	
	void del(ll pos)
	{
		for(ll i=0;i<(ll)v[pos].size();i++)
		{
			line[v[pos][i]]--;
			if(line[v[pos][i]]==1) Delete(a[v[pos][i]]);
		}
	}
	
	void move(ll pos)
	{
		if(vis[pos]) del(pos);
		else add(pos);
		vis[pos]^=1;
	}
	
	ll query(ll k)
	{
		ll i,res=0;
		for(i=1;i<=num;i++)
		{
			res+=sum[i];
			if(res>=k) break;
		}
		for(ll j=R[i];j>=L[i];j--)
		{
			if(res>k-gs[j]&&res<=k) return b[j];
			res-=gs[j];
		}
	}
	
	void work()
	{
		ll x,y;
		scanf("%lld%lld%lld",&n,&m,&q);
		for(ll i=1;i<n;i++)
		{
			scanf("%lld%lld",&x,&y);
			add(x,y);
			add(y,x);
		}
		for(ll i=1;i<=m;i++)
		{
			scanf("%lld%lld",&x,&y);
			scanf("%lld",&a[i]);b[i]=a[i];
			v[x].push_back(i);
			v[y].push_back(i);
		}
		sort(b+1,b+m+1);
		size=unique(b+1,b+m+1)-(b+1);
		for(ll i=1;i<=m;i++)
		{
			a[i]=lower_bound(b+1,b+size+1,a[i])-b;
		}
		sz=sqrt(m);
		num=m/sz;
		if(m%sz) num++;
		for(ll i=1;i<=m;i++) L[i]=m+1;
		for(ll i=1;i<=m;i++)
		{
			bel[i]=(i-1)/sz+1;
			L[bel[i]]=min(L[bel[i]],i);
			R[bel[i]]=max(R[bel[i]],i);
		}
		dfs(1,0);
		for(ll i=1;i<=18;i++)
		{
			for(int j=1;j<=n;j++)
			{
				fa[j][i]=fa[fa[j][i-1]][i-1];
			}
		}
		block=pow(idx,2.0/3.0);
		for(ll i=1;i<=q;i++)
		{
			Q[i].id =i;
			scanf("%lld%lld%lld",&x,&y,&Q[i].k);
			if(in[x]>in[y]) swap(x,y);
			ll tmp=lca(x,y);
			if(tmp==x) Q[i].lca=0,Q[i].l=in[x],Q[i].r=in[y];
			else Q[i].lca=tmp,Q[i].l=out[x],Q[i].r=in[y];
		}
		sort(Q+1,Q+q+1,cmp1);
		l=1;
		for(ll i=1;i<=q;i++)
		{
			while(l>Q[i].l) move(id[--l]);
			while(r<Q[i].r) move(id[++r]);
			while(l<Q[i].l) move(id[l++]);
			while(r>Q[i].r) move(id[r--]);
			if(Q[i].lca) move(Q[i].lca);
			ans[Q[i].id]=query(Q[i].k);
			if(Q[i].lca) move(Q[i].lca);
		}
		for(ll i=1;i<=q;i++)
		{
			printf("%lld\n",ans[i]);
		}
	}
	
}

int main()
{
	zzc::work();
	return 0;
}
上一篇:洛谷网校:树形问题(持续更新)


下一篇:【模板】倍增LCA