codeforces 805F

本文介绍了一种名为Big-Small的思想,这是一种在处理树形结构数据时使用的分块优化方法。通过预处理每个节点的最大路径长度,并结合二分查找与前缀和技巧,实现了高效查询两棵树中所有节点对的最大距离。文章详细讨论了算法的设计思路及其实现细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

卢总教我们的big-small思想,神奇的一匹。也就是分块的思想。

首先一手树形DP吧每个树的的每个点的最远长度预处理出来。对于两棵树,每一对点u,v只有两种情况,maxlen=max(maxlenu,maxlenv),一种是u的最远路+v的最远路+1>maxlen,另一钟相反。于是我们只要枚举一棵树中的点,然后对另一棵树中的点进行二分处理,前缀和优化一下,两种情况就可以logn求和了了,所以 查询2棵树的复杂度是min(sizeu,sizev)*log(max(sizeu,sizev))。然而直接去暴力还是有可能超时的,比如u和v都是n/2,查询q次,瞬间tle。

topline=根号n,对于大于topline的树最多有根号n个,吧这些树两两预处理,最大复杂度是n^1.5*log(n^0.5)。

对于小于根号n的,则最多,根号n*log(n)*q。

网上还有做法是每次查询树x和y后就用map记录ans[x][y],然后下次再查到就直接输出。很有道理的样子,貌似卡不了。

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>
#include<cmath>
#include<map>
#define maxl 100010

using namespace std;

int n,m,q,cnt,tot,topline;
long long size[maxl],f[maxl],ehead[maxl],maxlen[maxl];
struct ed
{
	int to,nxt;
}e[maxl<<1];
struct node 
{
	long long maxfur,maxind,secfur,secind;
}a[maxl];
vector <long long> aa[maxl],aafur[maxl],fursum[maxl];
vector <int> bigaa;
map <int,double> preans[maxl];
bool vis[maxl];

void dfs(int u,int k)
{
	int v;
	aa[k].push_back(u);
	vis[u]=true;size[k]++;f[u]=k;
	for(int i=ehead[u];i;i=e[i].nxt)
	{
		v=e[i].to;
		if(!vis[v])
		{
			dfs(v,k);
			if(a[v].maxfur+1>a[u].maxfur)
			{
				a[u].secfur=a[u].maxfur;a[u].secind=a[u].maxind;
				a[u].maxfur=a[v].maxfur+1;a[u].maxind=v;
			}
			else if(a[v].maxfur+1>a[u].secfur)
				a[u].secfur=a[v].maxfur+1,a[u].secind=v;
		}
	}
}

void dfs2(int u,int fa)
{
	int v;vis[u]=true;
	if(a[fa].maxind!=u && fa)
	{	
		if(a[fa].maxfur+1>a[u].maxfur)
		{
			a[u].secfur=a[u].maxfur;a[u].secind=a[u].maxind;
			a[u].maxfur=a[fa].maxfur+1;a[u].maxind=fa;
		}
		else if(a[fa].maxfur+1>a[u].secfur)
				a[u].secfur=a[fa].maxfur+1,a[u].secind=fa;
	}
	if(a[fa].secind!=u && fa)
	{
		if(a[fa].secfur+1>a[u].maxfur)
		{
			a[u].secfur=a[u].maxfur;a[u].secind=a[u].maxind;
			a[u].maxfur=a[fa].secfur+1;a[u].maxind=fa;
		}
		else if(a[fa].secfur+1>a[u].secfur)
			a[u].secfur=a[fa].secfur+1,a[u].secind=fa;
	}
	for(int i=ehead[u];i;i=e[i].nxt)
	{
		v=e[i].to;
		if(!vis[v])
			dfs2(v,u);
	}
}

void prework()
{
	memset(a,0,sizeof(a));
	memset(ehead,0,sizeof(ehead));
	for(int i=1;i<=tot;i++)
		aa[i].clear(),aafur[i].clear(),preans[i].clear(),
		fursum[i].clear();
	cnt=0;tot=0;
	int u,v;
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&u,&v);
		e[++cnt].to=v;e[cnt].nxt=ehead[u];ehead[u]=cnt;
		e[++cnt].to=u;e[cnt].nxt=ehead[v];ehead[v]=cnt;
	}
	for(int i=1;i<=n;i++)
		f[i]=i;
	memset(vis,0,sizeof(vis));
	for(int i=1;i<=n;i++)
	if(!vis[i])
		dfs(i,++tot);
	memset(vis,0,sizeof(vis));
	for(int i=1;i<=n;i++)
	if(!vis[i])
		dfs2(i,0);
	topline=ceil(sqrt(n*1.0));
	for(int i=1;i<=tot;i++)
	if(size[i]>=topline)
		bigaa.push_back(i);
	int l;
	for(int i=1;i<=tot;i++)
	{
		l=aa[i].size();
		for(int j=0;j<l;j++)
			aafur[i].push_back(a[aa[i][j]].maxfur);
	}
	for(int i=1;i<=tot;i++)
		sort(aafur[i].begin(),aafur[i].end()),maxlen[i]=aafur[i][size[i]-1];
	for(int i=1;i<=tot;i++)
	{
		l=aa[i].size();
		fursum[i].push_back(aafur[i][0]);
		for(int j=1;j<l;j++)
			fursum[i].push_back(fursum[i][j-1]+aafur[i][j]);
	}
}

void getpreans()
{
	int l=bigaa.size(),u,v,lu,lv,x,left,right;
	long long sum;
	double ans;
	for(int i=0;i<l;i++)
		for(int j=i+1;j<l;j++)
		{
			sum=0;
			u=bigaa[i];v=bigaa[j];
			if(size[u]>size[v])
				swap(u,v);
			lu=aa[u].size();lv=aa[v].size();
			for(int ii=0;ii<lu;ii++)
			{
				x=aa[u][ii];
				left=lower_bound(aafur[v].begin(),aafur[v].end(),
							max(maxlen[u],maxlen[v])-a[x].maxfur)-
								aafur[v].begin();
					if(left-1<0)
					sum+=fursum[v][lv-1]+(lv-left)*(a[x].maxfur+1);
				else
					sum+=fursum[v][lv-1]-fursum[v][left-1]+(lv-left)*(a[x].maxfur+1);
				sum+=(left-1+1)*max(maxlen[u],maxlen[v]);
			}
			ans=(double)sum/((double)size[u]*size[v]);
			preans[u][v]=ans;preans[v][u]=ans;
		}
}

void mainwork()
{
	int u,v,lu,lv,x,left,right;
	long long sum;
	double ans;
	getpreans();
	for(int i=1;i<=q;i++)
	{
		scanf("%d%d",&u,&v);
		u=f[u];v=f[v];
		if(u==v)
			printf("-1\n");
		else
			if(size[u]>=topline && size[v]>=topline)
				printf("%.10f\n",preans[u][v]);
		else
		{
			sum=0;
			if(size[u]>size[v])
				swap(u,v);
			lu=aa[u].size();lv=aa[v].size();
			for(int ii=0;ii<lu;ii++)
			{
				x=aa[u][ii];
				left=lower_bound(aafur[v].begin(),aafur[v].end(),
							max(maxlen[u],maxlen[v])-a[x].maxfur)-
								aafur[v].begin();
				if(left-1<0)
					sum+=fursum[v][lv-1]+(lv-left)*(a[x].maxfur+1);
				else
					sum+=fursum[v][lv-1]-fursum[v][left-1]+(lv-left)*(a[x].maxfur+1);
				sum+=(left-1+1)*max(maxlen[u],maxlen[v]);
			}
			ans=(double)sum/((double)size[u]*size[v]);
			printf("%.10f\n",ans);
		}
	}
}

int main()
{
	while(~scanf("%d%d%d",&n,&m,&q))
	{
		prework();
		mainwork();
	}
	return 0;
}




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值