联考20200618 T2 分岔路口
题目:
分析:
神仙期望,神仙结论:
对于一个点,肯定先随机跳再一路走到终点,这样是最优的
一个点向终点走了之后再随机跳,不会比直接随机跳更优
一个点的期望只与他到终点的距离有关
我们想找到一个阈值\(D\),使得离终点小于等于\(D\)的点直接走最优
显然可以二分,可是如何判断?
我们算出距离大于\(mid\)的点随机跳到小于等于\(mid\)的点的期望步数,加上直接走的期望步数\(x\)
如果小于\(mid\)说明还要多进行随机跳,将\(mid\)缩小,否则增大
期望步数\(x\)实际上是距离和除以点数,这个需要动态点分治维护一下
复杂度\(O(nlog^{2}n)\)
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<vector>
#define maxn 400005
#define MOD 998244353
#define INF 0x3f3f3f3f
using namespace std;
inline int getint()
{
int num=0,flag=1;char c;
while((c=getchar())<'0'||c>'9')if(c=='-')flag=-1;
while(c>='0'&&c<='9')num=num*10+c-48,c=getchar();
return num*flag;
}
int n,q;
int fir[maxn],nxt[maxn],to[maxn],cnt;
int f[18][maxn],dpt[maxn];
int Sz[maxn],Dpt[maxn],Fa[maxn],vis[maxn];
int dis[18][maxn];
vector<long long>sz[maxn],fsz[maxn],sum[maxn],fsum[maxn];
inline void newnode(int u,int v)
{to[++cnt]=v,nxt[cnt]=fir[u],fir[u]=cnt;}
inline void getsz(int u,int lst,int N,int &mn,int &rt)
{
Sz[u]=1;int mx=0;
for(int i=fir[u];i;i=nxt[i])if(!vis[to[i]]&&to[i]!=lst)
{
getsz(to[i],u,N,mn,rt),Sz[u]+=Sz[to[i]];
mx=max(mx,Sz[to[i]]);
}
mx=max(mx,N-Sz[u]);
if(mx<mn)mn=mx,rt=u;
}
inline int getrt(int u,int N)
{
int mn=INF,rt;
getsz(u,0,N,mn,rt);return rt;
}
inline void getp(int u,int lst,int d,vector<long long>&sz,vector<long long>&sum)
{
while(d>=sz.size())sz.push_back(0);
while(d>=sum.size())sum.push_back(0);
sz[d]++,sum[d]+=d,Sz[u]=1;
for(int i=fir[u];i;i=nxt[i])if(to[i]!=lst&&!vis[to[i]])
getp(to[i],u,d+1,sz,sum),Sz[u]+=Sz[to[i]];
}
inline void getdis(int u,int lst,int d,int id)
{
dis[id][u]=d;
for(int i=fir[u];i;i=nxt[i])if(to[i]!=lst&&!vis[to[i]])
getdis(to[i],u,d+1,id);
}
inline void solve(int u,int d)
{
vis[u]=1,Dpt[u]=d;
getp(u,0,0,sz[u],sum[u]);
getdis(u,0,0,d);
for(int i=1;i<sz[u].size();i++)
sz[u][i]+=sz[u][i-1],sum[u][i]+=sum[u][i-1];
for(int i=fir[u];i;i=nxt[i])if(!vis[to[i]])
{
int t=getrt(to[i],Sz[to[i]]);
Fa[t]=u;
getp(to[i],0,1,fsz[t],fsum[t]);
for(int i=1;i<fsz[t].size();i++)
fsz[t][i]+=fsz[t][i-1],fsum[t][i]+=fsum[t][i-1];
solve(t,d+1);
}
}
inline void dfs(int u)
{
for(int i=fir[u];i;i=nxt[i])if(to[i]!=f[0][u])
f[0][to[i]]=u,dpt[to[i]]=dpt[u]+1,dfs(to[i]);
}
inline int LCA(int u,int v)
{
if(dpt[u]<dpt[v])swap(u,v);
for(int i=17;~i;i--)if((dpt[u]-dpt[v])&(1<<i))u=f[i][u];
if(u==v)return u;
for(int i=17;~i;i--)if(f[i][u]!=f[i][v])u=f[i][u],v=f[i][v];
return f[0][u];
}
inline pair<long long,long long>query(int p,int x)
{
long long ret1=0,ret2=0;
int u=p,pr=0;
while(p)
{
if(x-dis[Dpt[p]][u]>=0)
{
int t=x-dis[Dpt[p]][u];
ret1+=sz[p][min((int)sz[p].size()-1,t)];
ret2+=sum[p][min((int)sum[p].size()-1,t)]+sz[p][min((int)sz[p].size()-1,t)]*dis[Dpt[p]][u];
if(pr)
{
ret1-=fsz[pr][min((int)fsz[pr].size()-1,t)];
ret2-=fsum[pr][min((int)fsum[pr].size()-1,t)]+fsz[pr][min((int)fsz[pr].size()-1,t)]*dis[Dpt[p]][u];
}
}
pr=p,p=Fa[p];
}
return make_pair(ret1,ret2);
}
int main()
{
n=getint(),q=getint();
for(int i=1;i<n;i++)
{
int u=getint(),v=getint();
newnode(u,v),newnode(v,u);
}
solve(getrt(1,n),0);
dfs(1);
for(int j=1;j<18;j++)for(int i=1;i<=n;i++)f[j][i]=f[j-1][f[j-1][i]];
while(q--)
{
int x=getint(),y=getint();
int L=0,R=n,mid;
while(L<R)
{
mid=(L+R+1)>>1;
pair<long long,long long>t=query(y,mid);
double g=1.0*(t.second+n-t.first)/t.first;
if(g+1<mid)R=mid-1;
else L=mid;
}
pair<long long,long long>t=query(y,L);
double g=1.0*(t.second+n-t.first)/t.first;
printf("%.8lf\n",min(g+1,1.0*dpt[x]+dpt[y]-2*dpt[LCA(x,y)]));
}
}