1 条题解

  • 0
    @ 2025-8-24 21:55:38

    自动搬运

    查看原文

    来自洛谷,原作者为

    avatar SuperJvRuo
    **

    搬运于2025-08-24 21:55:38,当前版本为作者最后更新于2018-10-28 12:05:46,作者可能在搬运后再次修改,您可在原文处查看最新版

    自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多

    以下是正文


    本篇题解符号约定:

    物理量 符号
    电流 I
    电动势 E
    电阻 R
    电势差(电压) U
    电势 φ\varphi

    这是一道电学题,我们复习一些电学内容:

    1.欧姆定律

    在同一电路中,通过某段导体的电流跟这段导体两端的电压成正比,跟这段导体的电阻成反比,即:

    I=URI=\frac{U}{R}

    2.基尔霍夫第一定律

    汇合于任一节点处的各电流的代数和等于0,即:

    I=Iin+Iout=0\sum I=\sum I_{in}+\sum I_{out}=0

    对于一些电学题目,可以依据欧姆定律和基尔霍夫第一定律列出方程组,利用高斯消元求解。

    但是这题数据范围50000,高斯消元只能获得30pts。我们注意到树上的最长链长度不超过50,应考虑由父亲到儿子或是儿子到父亲的递推关系,暴力递推答案。

    显然,只要本题中所有的电阻器阻值相同,不论每个电阻的阻值是10000Ω10000\Omega还是0.1Ω0.1\Omega,答案都是一样的。因此我将每个电阻的阻值记为RR。为了便于计算,钦点一个叶子节点为根。

    考虑节点ii

    ii的父节点为fafa,子节点集合为sonsonfafaii的电流为IiI_i

    由父节点fafaii的电路中,电阻处电势的下降,加上电源带来的电势上升,其结果就等于两点的电势差:

    IiR+Ei=φfaφiI_iR+E_i=\varphi_{fa}-\varphi_{i}

    移项得:

    Ii=φfaφiEiRI_i=\frac{\varphi_{fa}-\varphi_{i}-E_i}{R}

    同理,由ii到每个子节点xx的电路中,都有:

    IxR+Ex=φiφxI_xR+E_x=\varphi_{i}-\varphi_{x}

    移项,对所有子节点求和得:

    $$\sum_{x\in son}I_x=\sum_{x\in son}\frac{\varphi_{i}-\varphi_{x}-E_x}{R} $$

    ii点的基尔霍夫第一定律:

    xsonIx=Ii\sum_{x\in son}I_x=I_i

    将已求得的两个II带入第三个式子:

    $$\sum_{x\in son}\frac{\varphi_{i}-\varphi_{x}-E_x}{R}=\frac{\varphi_{fa}-\varphi_{i}-E_i}{R} $$

    整理得:

    $$\varphi_i=\frac{\varphi_{fa}-E_i+\sum_{x\in son}(\varphi_x+E_x)}{deg_i} $$

    为了暴力递推,设φi=Kiφfa+Bi\varphi_i=K_i\varphi_{fa}+B_i,其中KiK_iBiB_i都是只与iisonson有关的系数,将上式中的φx\varphi_x以这种形式表示,即得:

    $$\varphi_i=\frac{\varphi_{fa}-E_i+\sum_{x\in son}(K_x\varphi_i+B_x+E_x)}{deg_i} $$

    提出系数:

    Ki=1degixsonKxK_i=\frac{1}{deg_i-\sum_{x\in son}K_x} $$B_i=\frac{\sum_{x\in son}(B_x+E_x)-E_i}{deg_i-\sum_{x\in son}K_x}=K_i(\sum_{x\in son}(B_x+E_x)-E_i) $$

    豁然开朗了是不是?

    诶不对啊,我们要求的是ii到地面的电势差啊?我们还要思考一下接地节点的边界处理。

    叶子节点ii在原图中的度数为1,算上接地的一条边,度数为2。地面的电势为00。因此:

    φi=φfaEi2\varphi_i=\frac{\varphi_{fa}-E_i}{2}

    故:

    Ki=12K_i=\frac{1}{2} Bi=Ei2B_i=\frac{-E_i}{2}

    这样,我们求得的φi\varphi_i就是ii点到地面的电势差了。

    KK与电路中的电源无关,可以预处理直接求。加电源的时候暴力向上修改BB即可。查询电势差的时候也是向上dark♂力扫一遍。由于链长不超过50,其复杂度完全正确。

    #include<cstdio>
    #include<cctype>
    #include<algorithm>
    
    struct Edge
    {
        int to,next;
    }edge[100005];
    int head[50005],cnt;
    
    void Add_edge(int u,int v)
    {
        edge[++cnt]=(Edge){v,head[u]};
        head[u]=cnt;
        edge[++cnt]=(Edge){u,head[v]};
        head[v]=cnt;
    }
    
    int root;
    
    int degree[50005],fa[50005];
    
    double k[50005],b[50005],sum_b[50005],U[50005],sum_u[50005];
    
    void init_k(int u,int f)
    {
        fa[u]=f;
        double sum_k=0;
        for(int i=head[u];i;i=edge[i].next)
        {
            int v=edge[i].to;
            if(v!=f)
            {
                init_k(v,u);
                sum_k+=k[v];
            }
        }
        k[u]=1.0/(degree[u]-sum_k);
    }
    
    void Add_b(int u,int e)
    {
        if(u==0)
            return;
        else
        {
            sum_b[fa[u]]-=b[u];
            b[u]=(sum_b[u]+sum_u[u]-U[u])*k[u];
            sum_b[fa[u]]+=b[u];
            Add_b(fa[u],e);
        }
    }
    
    void Modify(int u,int v,int e)
    {
        if(fa[v]==u)
        {
            std::swap(u,v);
            e=-e;
        }
        U[u]+=e;
        sum_u[v]+=e;
        Add_b(u,e);
    }
    
    double Query(int u)
    {
        if(u==0)
        {
            return 0;
        }
        return k[u]*Query(fa[u])+b[u];
    }
    
    int main()
    {
        int n,m,u,v,e;
        scanf("%d%d",&n,&m);
        for(int i=1;i<n;++i)
        {
            scanf("%d%d",&u,&v);
            Add_edge(u,v);
            ++degree[u];
            ++degree[v];
        }
        
        for(int i=1;i<=n;++i)
        {
            if(degree[i]==1)
            {
                Add_edge(i,0);
                degree[0]=1;
                break;
            }
        }
        for(int i=1;i<=n;++i)
        {
            if(degree[i]==1)
                degree[i]=2;
        }
        init_k(0,0);
        
        while(m--)
        {
            char ch=getchar();
            while(!isalpha(ch))
            {
                ch=getchar();
            }
            if(ch=='Q')
            {
                scanf("%d",&u);
                printf("%.12lf\n",Query(u));
            }
            else
            {
                scanf("%d%d%d",&u,&v,&e);
                Modify(u,v,e);
            }
        }
        return 0;
    }
    
    • 1

    信息

    ID
    2972
    时间
    4000ms
    内存
    250MiB
    难度
    6
    标签
    递交数
    0
    已通过
    0
    上传者