【宝藏】题解(五校联考3day1)

31 篇文章 0 订阅
5 篇文章 0 订阅

分析

如果打爆搜的话可以拿60分。
首先知道期望是可以累加的,即i通过j去到k的期望,等于i去到j的期望加j去到k的期望。
所以令d[i]表示i的出度,F[i]表示从i到i的父亲的期望,G[i]表示i的父亲到i的期望,j表示i其中任意一个儿子,k表示i的父亲,l表示k其中任意一个儿子,e表示k的父亲。
很容易推出:

F[i]=1d[i]+1d[i](1+F[j]+F[i])

G[i]=1d[k]+1d[k](1+G[k]+G[i])+1d[k](1+F[l]+G[i])

简化后得
F[i]=F[j]+d[i]

G[i]=G[k]+F[l]+d[k]

然后分q次走,用倍增lca很容易算出vi到vi+1的期望,把期望累加就可以了。

#include <cmath>
#include <cstdio>
#include <cstdlib>
#include <iostream>
#include <algorithm>
#include <cstring>
using namespace std;
int d[600000],g[600000],f[600000],last[600000],next[600000],a[600000],dad[60000][50],deep[600000],fsum[600000],gsum[600000],m2[100];
int n,m,test,q,p,tot;
int bj(int x,int y)
{
    next[++tot]=last[x];
    last[x]=tot;
    a[tot]=y;
    d[x]++;
}
int dg(int x,int fa)
{
    deep[x]=deep[fa]+1;
    f[x]+=d[x];
    for(int i=last[x];i;i=next[i])
    {
        if(a[i]!=fa)
        {
            dg(a[i],x);
            f[x]+=f[a[i]];
        }
    }
}
int dg1(int x,int fa)
{
    int allj=0;
    for(int i=last[x];i;i=next[i])
    {
        if(a[i]!=fa)
        {
            allj+=f[a[i]];
        }
    }
    for(int i=last[x];i;i=next[i])
    {
        if(a[i]!=fa)
        {
            g[a[i]]=d[x]+g[x]+allj-f[a[i]];
        }
    }
    for(int i=last[x];i;i=next[i])
    {
        if(a[i]!=fa)
        {
            dg1(a[i],x);
        }
    }
}
int dg2(int x,int fa)
{
    dad[x][0]=fa;
    fsum[x]=fsum[fa]+f[x];
    gsum[x]=gsum[fa]+g[x];
    for(int i=last[x];i;i=next[i])
    {
        if(a[i]!=fa)
            dg2(a[i],x);
    }
}
int work(int x,int y,int z)
{
    return fsum[x]-fsum[z]+gsum[y]-gsum[z];
}
int lca(int x,int y)
{
    if(x==y) return 0;
    int l=0;
    if(deep[x]<deep[y])
    {
        l=x;
        x=y;
        y=l;
        l=10000000;
    }
    int i,xx=x,yy=y,j;
    if(deep[xx]>deep[yy])
    {
        j=int(log2(deep[xx]));
        for(i=j;i>=0;i--)
        {
            if(deep[dad[xx][i]]>deep[yy])
            {
                xx=dad[xx][i];
            }
        }
        xx=dad[xx][0];
    }
    if(xx==yy)
    {
        if(l==0) return work(x,y,xx);
        else return work(y,x,xx);
    }
    j=int(log2(deep[xx]));
    for(i=j;i>=0;i--)
    {
        if (dad[xx][i]!=dad[yy][i])
        {
            xx=dad[xx][i];
            yy=dad[yy][i];
        }
    }
    xx=dad[xx][0];
    if(l==0) return work(x,y,xx);
        else return work(y,x,xx);
}
int main()
{
    int i,j,k,l,x,y;
    m2[0]=1;
    for(i=1;i<=20;i++)
        m2[i]=m2[i-1]*2;
    scanf("%d",&test);
    while(test--)
    {   
        scanf("%d",&n);
        tot=0;
        memset(d,0,sizeof(d));
        memset(g,0,sizeof(g));
        memset(f,0,sizeof(f));
        memset(a,0,sizeof(a));
        memset(last,0,sizeof(last));
        memset(next,0,sizeof(next));
        memset(dad,0,sizeof(dad));
        memset(deep,0,sizeof(deep));
        memset(fsum,0,sizeof(fsum));
        memset(gsum,0,sizeof(gsum));
        for(i=1;i<=n-1;i++)
        {
            scanf("%d%d",&x,&y);
            bj(x,y);
            bj(y,x);
        }
        deep[0]=0;
        dg(0,0);
        dg1(0,0);
        dg2(0,0);
        for(j=1;j<=int(log2(n));j++)
        {
            for(i=1;i<=n;i++)
            {
                dad[i][j]=dad[dad[i][j-1]][j-1];
            }
        }
        scanf("%d",&q);
        for(i=1;i<=q;i++)
        {
            int ans=0;
            scanf("%d%d",&p,&x);
            for(j=1;j<=p;j++)
            {
                scanf("%d",&y);
                ans+=lca(x,y);
                x=y;
            }
            printf("%d.0000\n",ans);
        }
        cout<<endl;
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值