2018 湖南省队集训模拟试题( 七)T2分岔路口 ( 二分求期望 + 点分治卡常数 )

题意 : n个点的树,两种操作

1 : 走到相邻点。

2 : 等概率传送到任意一点

q次询问,问u到v在 最优策略 下的期望最少操作次数

正解 : 

先贪心,最优策略一定是先不停传送直到抽中一个比较好的点然后直接走到终点,因为先走后传送没有意义,是浪费。

那么我们一定可以找出一个点集S使得我们在S中的点时选择走路,反之传送。

那么我们传送到S中的点的期望步数等于传送到S中的点的概率的相反数。n/|S|

S的点走到终点的期望步数sigma(dis (i , tar)) / |S| (i∈S) (tar为终点)

总期望步数就是两个相加:

n/|S| +  sigma(dis (i , tar)) / |S| (i∈S)  >= K

 n + sigma(dis (i ,tar)) - K * |S| = 0

n + sigma(dis(i , tar) - K) = 0

sigma( K - dis(i , tar)) = n

可以发现S中的点一定满足dis(i,tar)<= K +1,满足此条件也必定在S中。

故|S|随K单不降,显然sigma( K - dis(i , tar))随K单不降,二分答案K再通过上述不等式求S,求sigma(K-dis(i,tar))与n的大小关系。

可以O(nlogn)求出K

但是终点有多个,于是想到利用树的性质,重复利用已算出的答案(sigma(dis(i,tar)))

求距离和,当然用点分治啦,不过是二分套点分治(常数巨大),开始卡常。

1 : 二分K时用整数二分,实数二分的话二分50次精度都不够,整数只需17次(想一想一次点分治的耗时),剩下的小数部分不会影响S,可以解一次方程(我要是出题人我就把可接受误差降到1e-12)

2 : 疯狂用空间换时间,不是有多次点分治吗 ?把相同的全部存起来,二分找dis(i,tar)<= K +1时不要用lower_bound,

手写二分,记得加inline(实测速度是lower_bound的1/2,实测优化之前一次点分治2000ms,火力全开优化后270ms)

3 : namespace是个好东西,妈妈再也不会担心我变量重名了。(我保守估计开了20个数组)

4 : 虽然原题时限4s,但是机房机子烂,标程都TLE了,可以适当放低要求。

5  : 这是我人生中第一个代码过350行的程序!!!!!

AC code:

#include<cstdio>
#include<cstring>
#include<cctype>
#include<algorithm>
#include<vector>
#define LL long long
#include<set>
#define eps (1e-9)
#define maxn 100005
using namespace std;

int n,q;
double f[maxn];
int info[maxn],Prev[maxn*2],to[maxn*2],cnt_e;
inline void Node(int u,int v){ Prev[++cnt_e]=info[u],info[u]=cnt_e,to[cnt_e]=v; }

int DIS[maxn*30],SUM[maxn*30],DIS2[maxn*30],SUM2[maxn*30],siz1,siz2;
int TOT[maxn],TOT2[maxn*30],siz3;
int dep[maxn],fa[maxn],son[maxn],siz[maxn],tp[maxn];

inline void dfs1(int now,int ff)
{
	dep[now]=dep[fa[now]=ff]+1;
	siz[now]=1;
	son[now]=-1;
	for(int i=info[now];i;i=Prev[i])
		if(to[i]!=ff)
		{
			dfs1(to[i],now);
			siz[now]+=siz[to[i]];
			if(son[now]==-1 || siz[to[i]] > siz[son[now]])son[now]=to[i];
		}
}

inline void dfs2(int now,int ff)
{
	if(son[now]!=-1) tp[son[now]]=tp[now],dfs2(son[now],now);
	for(int i=info[now];i;i=Prev[i])
		if(to[i]!=ff && to[i]!=son[now])
			tp[to[i]]=to[i],dfs2(to[i],now);
}

inline int Lca(int a,int b)
{
	for(;tp[a]!=tp[b];)
	{
		if(dep[tp[a]]<dep[tp[b]]) swap(a,b);
		a=fa[tp[a]];
	}
	if(dep[a]<dep[b]) return a;
	return b;
}

inline int dist(int u,int v){ return dep[u]+dep[v] - 2 * dep[Lca(u,v)]; }
/*
int seq[maxn];
double sum[maxn];
double solve(int v)
{
	double L=0,R=n,Mid;
	for(int i=1;i<=n;i++)
		seq[i]=dist(i,v);
	sort(seq+1,seq+1+n);
	for(int i=1;i<=n;i++)
		sum[i]=sum[i-1]+seq[i];
	for(;(R-L)>=eps;)
	{
		Mid=(L+R)*0.5;
		int u=upper_bound(seq+1,seq+1+n,Mid)-seq-1;
		if(sum[u]+1.0 * (n-u) * Mid<=(Mid-1)*n) //I wanna make some changes
			R=Mid;
		else 
			L=Mid+eps;
	}
	return L;
}
*/

LL L[maxn],R[maxn],Mid[maxn],had[maxn];

int Maxdep;
int rela[maxn];

namespace DFZ
{
	bool vis[maxn];
	int dfseq[maxn];
	int Min,rt;
	
	void Sert(int now ,int ff,int tsz)
	{
		int Max=0;
		siz[now]=1;
		for(int i=info[now];i;i=Prev[i])
			if(to[i]!=ff && !vis[to[i]])
			{
				Sert(to[i],now,tsz);
				Max=max(Max,siz[to[i]]);
				siz[now]+=siz[to[i]];
			}
		Max=max(Max,tsz-siz[now]);
		if(Max<Min || rt==-1)
			Min=Max,rt=now;
	}

	int Gert(int now,int tsz)
	{
		Min=0x3f3f3f3f,rt=-1;
		Sert(now,-1,tsz);
		return rt;
	}
	
	int dis[maxn],tot;
	double sum[maxn];
	void dfs(int now,int ff,int dist)
	{
		dis[++tot]=dist;
		for(int i=info[now];i;i=Prev[i])
			if(to[i]!=ff && !vis[to[i]])
				dfs(to[i],now,dist+1);
	}
	
	int dis2[maxn],tot2;
	double sum2[maxn];
	void ser(int now,int ff,int dist)
	{
		dis2[++tot2]=dist;
		for(int i=info[now];i;i=Prev[i])
			if(to[i]!=ff && !vis[to[i]])
				ser(to[i],now,dist+1);
	}
	
	int LLL=1,RR,mid;
	inline int sc1(double num)
	{
		LLL=1,RR=tot+1;
		while(LLL<RR)
		{
			mid=(LLL+RR)>>1;
			if(dis[mid]<=num) LLL=mid+1;
			else RR=mid;
		}
		return LLL;
	}
	
	inline int sc2(double num)
	{
		LLL=1,RR=tot2+1;
		while(LLL<RR)
		{
			mid=(LLL+RR)>>1;
			if(dis2[mid]<=num) LLL =mid+1;
			else RR=mid;
		}
		return LLL;
	}
	
	void solu(int now,int ff,int dist)
	{
		int u=sc1(Mid[now]-dist)-1,u2=sc2(Mid[now]-dist)-1;
		had[now]+=sum[u]+1.0 * dist * (u-u2)+1.0 * (tot-u-tot2+u2) * Mid[now]-sum2[u2];
		
		for(int i=info[now];i;i=Prev[i])
			if(to[i]!=ff && !vis[to[i]])
				solu(to[i],now,dist+1);
	}

	int us1=1,us2=1,us3=1;
	void solve(int num)
	{
		int now=dfseq[num];
		vis[now]=1;
		
		/*
		tot=0;
		dfs(now,0,0);
		sort(dis+1,dis+1+tot);
		for(int i=1;i<=tot;i++) sum[i]=sum[i-1]+dis[i];
		*/
		
		tot=TOT[now];
		for(int i=1;i<=tot;i++) 
		{
			dis[i]=DIS[us1];
			sum[i]=SUM[us1++];
		}
		
		int u=sc1(Mid[now])-1;
		had[now]+=sum[u]+1.0 * (tot-u) * Mid[now];
		
		for(int i=info[now];i;i=Prev[i])
		if(!vis[to[i]])
		{
			/*
			tot2=0;
			ser(to[i],now,1);
			siz[to[i]]=tot2;
			sort(dis2+1,dis2+1+tot2);
			for(int j=1;j<=tot2;j++) sum2[j]=sum2[j-1]+dis2[j];
			*/
			
			siz[to[i]]=tot2=TOT2[us3++];
			for(int j=1;j<=tot2;j++)
			{
				dis2[j]=DIS2[us2];
				sum2[j]=SUM2[us2++];
			}
			
			solu(to[i],now,1);
		}
		
		if(num%10==1)
			for(int i=1;i<=10 && i+num<=n;i++)
				solve(i+num);
	}
	
	int cnt_num=0;
	void Prepare(int now)
	{
		dfseq[++cnt_num]=now;
		vis[now]=1;
		
		tot=0;
		dfs(now,0,0);
		sort(dis+1,dis+1+tot);
		for(int i=1;i<=tot;i++)
		{
			sum[i]=sum[i-1]+dis[i];
			DIS[++siz1]=dis[i];
			SUM[siz1]=sum[i];
		}
		TOT[now]=tot;
		
		for(int i=info[now];i;i=Prev[i])
		if(!vis[to[i]])
		{
			tot2=0;
			ser(to[i],now,1);
			siz[to[i]]=tot2;
			sort(dis2+1,dis2+1+tot2);
			for(int j=1;j<=tot2;j++) 
			{
				sum2[j]=sum2[j-1]+dis2[j];
				DIS2[++siz2]=dis2[j];
				SUM2[siz2]=sum2[j];
			}
			
			TOT2[++siz3]=tot2;
		}
				
		for(int i=info[now];i;i=Prev[i])
			if(!vis[to[i]])
			{
				Prepare(Gert(to[i],siz[to[i]]));
			}
	}
	
	void solu2(int now,int ff,int dist)
	{
		rela[now]+=tot-sc1(L[now]-dist) - tot2 + sc2(L[now]-dist);
		
		for(int i=info[now];i;i=Prev[i])
			if(to[i]!=ff && !vis[to[i]])
				solu2(to[i],now,dist+1);
	}
	
	void solve2(int num)
	{
		int now=dfseq[num];
		vis[now]=1;
		tot=TOT[now];
		for(int i=1;i<=tot;i++) 
		{
			dis[i]=DIS[us1];
			sum[i]=SUM[us1++];
		}
		
		rela[now]+=tot-sc1(L[now])+1;
		
		for(int i=info[now];i;i=Prev[i])
		if(!vis[to[i]])
		{
			/*
			tot2=0;
			ser(to[i],now,1);
			siz[to[i]]=tot2;
			sort(dis2+1,dis2+1+tot2);
			for(int j=1;j<=tot2;j++) sum2[j]=sum2[j-1]+dis2[j];
			*/
			
			siz[to[i]]=tot2=TOT2[us3++];
			for(int j=1;j<=tot2;j++)
			{
				dis2[j]=DIS2[us2];
				sum2[j]=SUM2[us2++];
			}
			
			solu2(to[i],now,1);
		}
		
		if(num%10==1)
			for(int i=1;i<=10 && i+num<=n;i++)
				solve2(i+num);
	}
}
double ans[maxn];
	
inline void read(int &res)
{
	char ch;
	for(;!isdigit(ch=getchar()););
	for(res=ch-'0';isdigit(ch=getchar());res=res*10+ch-'0');
}

int main()
{
	freopen("branching.in", "r",stdin);
	freopen("branching.out","w",stdout);
	
	read(n),read(q);
	for(int i=1,u,v;i<n;i++)
	{
		read(u),read(v);
		Node(u,v),Node(v,u);
	}
	
	dfs1(1,0);
	tp[1]=1;
	dfs2(1,0);
	
	DFZ::Prepare(DFZ::Gert(1,n));
	
	for(int i=1;i<=n;i++) L[i]=0,R[i]=n;
	for(int i=1;i<=17;i++)
	{
		for(int i=1;i<=n;i++) 
			Mid[i]=int((L[i]+R[i]) * 0.5), had[i]=DFZ::vis[i]=0;
			
		
		DFZ::us1=DFZ::us2=DFZ::us3=1;
		DFZ::solve(1);
		
		for(int i=1;i<=n;i++)
			if(had[i]<=(Mid[i]-1) * n) R[i]=Mid[i];
			else L[i]=Mid[i];
		
	}
	
	DFZ::us1=DFZ::us2=DFZ::us3=1;
	memset(DFZ::vis,0,sizeof DFZ::vis);
	DFZ::solve2(1);
	for(int i=1;i<=n;i++)
	{
		had[i]-=rela[i] * Mid[i];
		ans[i] = 1.0 * ( n + had[i] ) / (n - rela[i]);
	}
	
	for(int u,v;q--;)
	{
		read(u),read(v);
		printf("%.8lf\n",min(1.0 * dist(u,v) , ans[v]));
	}
	
}

猜你喜欢

转载自blog.csdn.net/qq_35950004/article/details/80835760