cf894 D .Ralph And His Tour in Binary Country 二叉树性质

题意的连边描述很容易想到线段树,而线段树是个二叉树。

题意求的是,每次询问某个节点到其他所有节点的距离中,大于H的和是多少。

由于是只加大于H的,所以不能换根.

每次都要暴力查询。

不过我们可以预处理出来一些东西。

容易发现,预处理出某个节点到子树所有节点的距离很容易,求其中大于H的可以用二分查找。

而二叉树最多logn层,每层n个节点,空间最大nlogn。

所以直接建课树,节点存一个vector。这个操作很像归并线段树。

合并时可以归并排序合并,或者直接合并,最后sort一下即可。让每个节点维护到子树所有节点距离有序,再维护前缀和/

就能快速找到大于H的所有距离。

对于一次询问   A  H 

A到其他节点分为:

A的子树

A的父亲节点

A父亲节点的另一个儿子的子树。

类似换根搞一搞就行。

复杂度mlog^2 n

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
//typedef __int128 LL;
//typedef unsigned long long ull;
//#define F first
//#define S second
typedef long double ld;
typedef pair<int,int> pii;
typedef pair<ll,ll> pll;
typedef pair<ld,ld> pdd;
const ld PI=acos(-1);
const ld eps=1e-9;
//unordered_map<int,int>mp;
//#define ls (o<<1)
//#define rs (o<<1|1)
#define pb push_back
//#define a(i,j) a[(i)*(m+2)+(j)]  //m是矩阵的列数
//pop_back()
const int seed=131;
const int M = 1e6+7;
/*
int head[M],cnt;
void init(){cnt=0,memset(head,0,sizeof(head));}
struct EDGE{int to,nxt,val;}ee[M*2];
void add(int x,int y,int z){ee[++cnt].nxt=head[x],ee[cnt].to=y,ee[cnt].val=z,head[x]=cnt;}
*/
int fa[M],ls[M],rs[M];
ll L[M];//节点连向其父亲节点的边长
vector<ll>v[M];//每个节点开个vector,每层最多n,最多log层 所以最大空间nlogn
vector<ll>sm[M];
void dfs(int x)
{
	v[x].pb(0);
	if(ls[x])
	{
		dfs(ls[x]);
		for(int i=0;i<v[ls[x]].size();i++)v[x].pb(v[ls[x]][i]+L[ls[x]]);
	} 
	if(rs[x])
	{
		dfs(rs[x]);
		for(int i=0;i<v[rs[x]].size();i++)v[x].pb(v[rs[x]][i]+L[rs[x]]);
	}
}
ll cal(int id,ll d)//第id个节点子树中,到id距离小于等于d的  结果和 
{
	if(d<=0)return 0;
	ll ans=0;
	int tp=lower_bound(v[id].begin(),v[id].end(),d)-v[id].begin();
	//cout<<tp<<"_______________________"<<endl;
	if(tp>0)ans+=d*tp-sm[id][tp-1];
	return ans;
}
int main()
{
  	int n,m;
  	scanf("%d%d",&n,&m);
  	for(int i=1;i<=n;i++)
  	{
  		if(i*2<=n)ls[i]=i*2;
  		if(i*2+1<=n)rs[i]=i*2+1;
  		fa[i]=i/2;
	}
  	for(int i=1;i<n;i++)
  	{
  		ll x;
  		scanf("%lld",&x);
  		L[i+1]=x;
	}
	dfs(1);//预处理每个节点  到子树所有节点的距离 
	for(int i=1;i<=n;i++)
	{
		sort(v[i].begin(),v[i].end());
		sm[i].resize(v[i].size());
		ll tp=0;for(int j=0;j<sm[i].size();j++)tp+=v[i][j],sm[i][j]=tp;
	}
	while(m--)
	{
		ll A,H;
		scanf("%lld%lld",&A,&H);
		ll tp=A;
		ll id=lower_bound(v[A].begin(),v[A].end(),H)-v[A].begin();
		ll ans=0;
		if(id>0)ans+=H*id-sm[A][id-1];
		ll dis=0;//当前节点到A节点的距离 
		while(tp!=1&&H-dis>=0)
		{
			if(dis+L[tp]<=H)ans+=H-dis-L[tp];
			if(ls[fa[tp]]==tp)//当前节点是左儿子 ,少算了右儿子所有节点
			{
				if(rs[fa[tp]])ans+=cal(rs[fa[tp]],H-(dis+L[rs[fa[tp]]]+L[tp]));
			}
			else//当前节点是右儿子 
			{
				if(ls[fa[tp]])ans+=cal(ls[fa[tp]],H-(dis+L[ls[fa[tp]]]+L[tp]));
			}
			dis+=L[tp];
			tp=fa[tp];
		}
		printf("%lld\n",ans);
	}
	return 0;
}
发布了284 篇原创文章 · 获赞 13 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/bjfu170203101/article/details/104282835