オンライン裁判官:Codeforces629E、Luogu-CF629E
ラベル:木の数は、ソートルートのために、議論します
タイトル説明
n個のノードのツリーを考えます。そこMクエリは、Bは、我々は自由端に接続されていない接続された任意の2点とすることができるそれぞれ含む2つの数値を求め、であるので、その一緒にAと、このエッジは、Bは、ループ内にあります。各クエリに対して、このような環は、所望の長さを求めます。
\(2 <= N、M <= 10 ^ 5 \)
エントリー
最初の行は、それぞれ、二つの整数N、M、ノード及び問い合わせの数を含みます。
次に、二つの整数のN-1ラインがUは、Vは、VにUからエッジがあることを示します。
二つの整数、B(≠B)の次のMラインは 、 チャレンジを表します。
輸出
クエリごとに、ループの所望の長さの状態の出力。答えは小数点以下6桁を保持します。
サンプル
入力#1
4 3
2 4
4 1
3 2
3 1
2 3
4 1
出力#1
4.00000000
3.00000000
3.00000000
入力#2
3 3
1 2
1 3
1 2
1 3
2 3
出力#2
2.50000000
2.50000000
3.00000000
問題の解決策
タイトルシークは、実際には、二つのこと、見つける、望ましい\(すべて= \) 、リングの数を形成することができるが、\(ANS = \)リングの全ての合計で、両方の分割の答え。
ツリーに列挙。どちらの場合も、次の分の議論。
以下は、配列、およびその意義を説明するために使用されます。
\(SZ [X] \):Xは、ルートとするサブツリー内のノードの数が含まれています。
\(DEP [X] \) :奥行きノード。
\(FA [X] [I = 0..17] \) :アップセクションXの \(2 ^ I \)祖先(後ホップ乗算するLCA)。
\(SUM [X] \):\(DEP =Σ_} {son∈x[息子] \) 、深さの全ての点の、すなわちサブツリー和。
\(TOT [X-] \) :バックは言います。
1.(U、V)先祖の関係ではありません
私たちは、U、Vサブツリーポイントがリンクする見つけることができます。
したがって、環の数を形成することができる\(SZすべて= [U] * SZ [V] \) 、\(SZ \)アレイがサブツリーに含まれるノードの数を示します。
リングの全長さ:
\ [ANS = SZ + [U] *(SUM [V] -dep [V] * SZ [V]); \\ ANS = SZ + [V] *(SUM [U] -dep [ U] * SZ [U]) ; \\ ANS + =すべて*(DEP [U] + DEP [V] -2 * DEP [LCA] +1);(LCAは、LCAのVをUを意味する)\]
容易図から分かります。
2.(u、v)は先祖の関係であります
特別なポイントの説明:Uのような小さなドットの深さ。Uの息子は大手Vの1の息子です。vが大きい方の深さです。
息子を取得する方法は?最大ジャンプ乗算\(DEP [V] -dep [ U] -1 \) 工程を、O(logN個)の時間で得ることができます。
この図の下に見る、私たちはすることができ、vのサブツリー内の点を選択し、青色部分(ポイント他のサブツリー息子)環を形成し、両者を接続するために、点から選択されます。
したがって、リングの数\(すべて= SZ [V] *(SZ [1] -SZ [ソン])\) 。
どのようにループの長さの合計を見つけるには?以下のセクションは、サブステップ溶液に分割されます。
ここで、下記の別の要素のコンビニエンス表す\(SZ1 = SZ [V] \)、\ (SZ2 =(SZ [1] -SZ [息子])\) 。\(SZ1の\は)ピンクドットの一部であり、\(SZ2 \)は、青い点の一部です。
パート1:
\(ANS + =(和[V] -dep [V] * SZ1)* SZ2 +(DEP [V] -dep [U])*全て\)
その2:
\(年+ = 1 *すべて\)
その3:
この分析は、いくつかの前と比較して、良いことではありません。
距離ツリーポイントを見つけるために(B)は\(DEP [A] + DEP [B] -2 DEP * [LCA(B)] \) 。
すべて一緒に、我々は尋ねるので、今ではこのようなものを下回っています。
\ [発[U] * SZ2
+Σ_{x∈bluepart}(DEP [X] -2 * DEP [LCA(U、X)])\]は、 その前にOであることができる(1)を算出しました。コックの後ろの男はどのように実用的な時間を考え出すことですか?
プレ配列考える\(TOT [息子] \)がU親ノードである場合、次いで、示し(TOT [息子] =Σ_\ {x∈bluepart}(DEP [X] -2 * DEP [LCA (U、X)])\ ) 上記式の背面部です。
DFSは再び、完全な\(TOT \)前処理した配列は次のように進行します。マップまたはXを取得する際の十分な理解を描き、fはLCAのタイプです。
void dfs2(int x){
int f=fa[x][0];
if(f){
tot[x]=tot[f]+sum[f]-sum[x];
tot[x]-=2ll*(sz[f]-sz[x])*dep[f];
}
for(int i=head[x];i;i=e[i].nxt)if(e[i].to!=fa[x][0])dfs2(e[i].to);
}
議論2例結論として、時間計算量は\(O(N + M)\)を。
次のように完全なコードは次のとおりです。
//原题CF629E
#include<bits/stdc++.h>
#define int long long
using namespace std;
typedef long long ll;
const int N=1e5+10;
inline int read(){
int x=0;char c=getchar();
while(c<'0'||c>'9')c=getchar();
while(c>='0'&&c<='9')x=(x<<3)+(x<<1)+(c^48),c=getchar();
return x;
}
struct edge{
int to,nxt;
}e[N<<1];
int head[N],ecnt;
inline void link(int u,int v){
e[++ecnt].to=v,e[ecnt].nxt=head[u];
head[u]=ecnt;
}
int n,m;
ll sum[N],tot[N];
int sz[N],dep[N],fa[N][18];
ll gcd(ll a,ll b){return b==0?a:gcd(b,a%b);}
void dfs(int x,int f){
fa[x][0]=f,dep[x]=dep[f]+1,sum[x]=dep[x];
sz[x]=1;
for(int i=head[x];i;i=e[i].nxt){
int y=e[i].to;if(y==f)continue;
dfs(y,x);
sz[x]+=sz[y];
sum[x]+=sum[y];
}
}
void dfs2(int x){
int f=fa[x][0];
if(f){
tot[x]=tot[f]+sum[f]-sum[x];
tot[x]-=2ll*(sz[f]-sz[x])*dep[f];
}
for(int i=head[x];i;i=e[i].nxt)if(e[i].to!=fa[x][0])dfs2(e[i].to);
}
inline int jump(int x,int stp){
for(int i=0;i<=17;i++)if(stp&(1<<i))x=fa[x][i];
return x;
}
inline int LCA(int x,int y){
if(dep[x]<dep[y])swap(x,y);
int stp=dep[x]-dep[y];
x=jump(x,stp);
if(x==y)return x;
for(int i=17;i>=0;i--)if(fa[x][i]!=fa[y][i])x=fa[x][i],y=fa[y][i];
return fa[x][0];
}
signed main(){
n=read(),m=read();
for(int i=1;i<n;i++){
int u=read(),v=read();
link(u,v),link(v,u);
}
dfs(1,0),dfs2(1);
for(int j=1;j<=17;j++)for(int i=1;i<=n;i++)fa[i][j]=fa[fa[i][j-1]][j-1];
while(m--){
int u=read(),v=read();
if(dep[u]>dep[v])swap(u,v);
int lca=LCA(u,v);
ll all,ans;
if(lca==u){
int son=jump(v,dep[v]-dep[u]-1);
int sz1=sz[v],sz2=sz[1]-sz[son];
all=1ll*sz1*sz2,ans=0;
ans+=1ll*(sum[v]-dep[v]*sz1)*sz2;
ans+=1ll*(tot[son]+sz2*dep[u])*sz1;
ans+=1ll*all*(dep[v]-dep[u]+1);
}
else{
all=1ll*sz[u]*sz[v],ans=0;
ans+=1ll*sz[u]*(sum[v]-dep[v]*sz[v]);
ans+=1ll*sz[v]*(sum[u]-dep[u]*sz[u]);
ans+=1ll*all*(dep[u]+dep[v]-2*dep[lca]+1);
}
//ll g=gcd(all,ans);
//printf("%lld/%lld\n",ans/g,all/g);
printf("%.7f\n",1.0*ans/all);
}
}