804D - Expected diameter of a tree

太久之前寫的都快忘了= = 這題做起來還是挺簡單的。當時很快就想出來寫完了(調好久我會亂說嘛)
題意即
給你一個森林,每次詢問給出u,v,
從u所在連通塊中隨機選出一個點與v所在連通塊中隨機選出一個點相連,
問你此時相連出的樹的直徑期望是多少?(如果本身就在同一個連通塊內,則輸出-1)

仔細想一下就知道答案只有兩種情況,大樹的直徑或兩個樹鏈接起來。
求每個連通塊直徑後從兩端點找到最長的一個距離。
然後因爲答案只有那兩種情況 考慮如何算期望。就是一個點會等概率匹配所有點,可以按長度算後綴和後我記得是On 掃一遍計算答案。
= =總之這題就是個暴力思路很好想 大力碼代碼就好了

#include<bits/stdc++.h>
using namespace std;

const int MAXN=1e5+5;

typedef pair<int,int>par;
#define mp make_pair
#define ll long long 

struct edge{
    ll to,next,w;
}e[MAXN<<1];

ll head[MAXN],cnt=0;

inline void add(ll u,ll v){
    e[++cnt]=(edge){v,head[u]},head[u]=cnt;
    e[++cnt]=(edge){u,head[v]},head[v]=cnt;
}

map<par,double>ma;

ll n,m,q,belong[MAXN],tim=0;
bool vis[MAXN],vis2[MAXN];

struct data{
    ll maxdis; 
    vector<ll>dissum,dissz;
}d[MAXN];

ll who=0,whodis=0;
ll s[MAXN],top=0,dist[MAXN][4],ly;

void dfs(ll u,ll fa,ll dis){
    if(dis>whodis)whodis=dis,who=u;
    s[++top]=u;
    for(ll i=head[u];i;i=e[i].next){
        ll v=e[i].to;
        if(v==fa)continue;
        dfs(v,u,dis+1);
    }
}

void dfs2(ll u,ll fa,ll num){
    if(dist[u][num]>whodis)whodis=dist[u][num],who=u;
    if(num==2)dist[u][3]=max(dist[u][1],dist[u][2]),ly=max(ly,dist[u][3]);
    for(ll i=head[u];i;i=e[i].next){
        ll v=e[i].to;
        if(v==fa)continue;
        dist[v][num]=dist[u][num]+1;
        dfs2(v,u,num);
    }
}

void get(ll x){
    who=x;whodis=0;top=0,ly=0;
    dfs(x,x,0);
    ll l=who;whodis=0;dist[l][1]=0;
    dfs2(l,l,1);ll r=who;dist[r][2]=0;
    dfs2(r,r,2);++tim;
    for(ll i=0;i<=ly+1;i++)d[tim].dissum.push_back(0),d[tim].dissz.push_back(0);
    d[tim].maxdis=ly; 
//  for(ll i=top;i;i--)cout<<s[i]<<":"<<dist[s[i]][3]<<"ok"<<endl;
//  cout<<endl;
    for(ll i=top;i;i--)belong[s[i]]=tim,d[tim].dissz[dist[s[i]][3]]+=1,d[tim].dissum[dist[s[i]][3]]+=dist[s[i]][3];//後綴和 
    for(ll i=ly;i>=1;i--)d[tim].dissz[i-1]+=d[tim].dissz[i],d[tim].dissum[i-1]+=d[tim].dissum[i];
//  for(ll i=0;i<=ly+1;i++)cout<<d[tim].dissz[i]<<" ";cout<<endl;
//  for(ll i=0;i<=ly+1;i++)cout<<d[tim].dissum[i]<<" ";cout<<endl;
}

double solve(ll a,ll b){//a是大塊 b是小塊 
    ll ans=0;
    ll maxlen=d[a].maxdis;
    ll minlen=d[b].maxdis;
    if(maxlen==0)return 1.0;
    for(ll i=maxlen;i>=maxlen-1;i--){
        ans+=((i+1)*d[b].dissz[0]+d[b].dissum[0])*(d[a].dissz[i]-d[a].dissz[i+1]);
    }
    ll where=0,where2=maxlen-1,flag=0;
    //if(a==1&&b==4)cout<<maxlen<<" "<<minlen<<"okok"<<endl;
    for(ll i=maxlen-2;i>=1;i--){
        flag=1;
        where2--;++where;if(where>minlen)break;
        //ans+=(d[a].dissum[i]-d[a].dissum[i+1])*d[b].dissz[where]+d[b].dissum[where]+d[b].dissz[where]*(d[a].dissz[i]-d[a].dissz[i+1]);
    //  cout<<(d[a].dissz[i]-d[a].dissz[i+1])<<"orztata momomo";
        ans+=((i+1)*d[b].dissz[where]+d[b].dissum[where])*(d[a].dissz[i]-d[a].dissz[i+1]);
        ans+=maxlen*(d[b].dissz[0]-d[b].dissz[where])*(d[a].dissz[i]-d[a].dissz[i+1])   ;
        //cout<<(d[b].dissz[0]-d[b].dissz[where])<<"orzd2 momomo";
    //  cout<<ans<<"ok";
    }
//  cout<<ans<<"ok";
//  cout<<where2<<"ok";
    if(flag)ans+=maxlen*(d[a].dissz[0]-d[a].dissz[where2+1])*d[b].dissz[0];
//  cout<<(double)ans/(d[a].dissz[0]*d[b].dissz[0]);
    return (double)ans/(d[a].dissz[0]*d[b].dissz[0]);
}

int main(){
    scanf("%I64d%I64d%I64d",&n,&m,&q);
    for(ll i=1;i<=m;i++){
        ll u,v;
        scanf("%I64d%I64d",&u,&v);
        add(u,v);
    }
    for(ll i=1;i<=q;i++){
        ll u,v;
        scanf("%I64d%I64d",&u,&v);
        if(!belong[u])get(u);if(!belong[v])get(v);  
        ll a=belong[u],b=belong[v];
        if(a!=b){
            if(d[a].maxdis<d[b].maxdis)swap(a,b);
            if(!ma[mp(a,b)]){
                ma[mp(a,b)]=solve(a,b);
            }
            printf("%.10lf\n",ma[mp(a,b)]);
        }
        else printf("-1\n");
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章