long1的博客

人在江湖,身不由己

EOJ Monthly 2017.12 不见了的人口数据 (Hard)

题目链接

题意: 一棵树树上节点都有一个值ai,都条边的长度为1,现在知道每个节点的代价值
     为 all 节点t与它的距离即t节点的值积的和pi,问所有的ai的值.(0<=ai<=1000,n<=250000)

p[i]=j=1ndist(i,j)×aj,distijij.

*以前一直觉得是道Guass消元题,但是n有点大,系数矩阵都不能存,更不用说O(n^3)的时间复杂度。今天看了,感觉跟以前做过的树题很相似,细细推导,发觉是道子树的和的技巧题。

*不妨记为root为1的有根树,那么sum[i]表示根为节点i的子树节点值a的和。

p[1]=i=2nsum[i].

uvsum[1]2×sum[v]=p[v]p[u]

因为v为根的子树上的所有节点(包括v)距离u的dist比距离v的dist多1,而非v子树上的点距离u的dist比距离v的dist少1,所有v的代价比u的代价多一颗子树上的值和sum[v],而少非子树上的值和(sum[1]-sum[v])。

所以我们需要做一次DFS,将所有p[son]-fa[fa]之和求出,然后联立第一个公式:得到

(n1)×sum[1]2×p[1]=all(p[son]p[fa]).

sum[1]即解出,在做一个dfs,解出sum[v],然后减去所有回溯的子树权值和,得到了a[v].

#include <iostream>
#include <stdio.h>
#include <string.h>
#include <vector>
#include <algorithm>
#define llt long long
using namespace std;

const int Size=25*1e4+10;
/*
  子树和 技巧题
  以rt为根的子树上所有节点的值的和为 sum[rt]
  所有
*/
vector <int> V[Size];
llt p[Size],sum[Size]={0},a[Size];//sum[1] 表示整棵以1为根的树节点总和;

//求出a1+a2+……+an 先找出所有节点代价与父节点的代价差
llt DFS(int rt,int fa){
    llt tot=0;
    for(int i=0;i<V[rt].size();++i){
        int son=V[rt][i];
        if(fa==son) continue;
        tot+=(p[son]-p[rt])+DFS(son,rt);
    }
    return tot;
}
llt dfs(int rt,int fa){

    if(rt!=1) sum[rt]=(sum[1]+p[fa]-p[rt])/2;
    a[rt]=sum[rt];
    for(int i=0;i<V[rt].size();++i){
        if(V[rt][i]==fa) continue;
        a[rt]-=dfs(V[rt][i],rt);
    }
    return sum[rt];
}
int main(){
    int n;
    scanf("%d",&n);

    for(int i=1;i<n;++i){
        int u,v;
        scanf("%d%d",&u,&v);
        V[u].push_back(v);
        V[v].push_back(u);
    }

    for(int i=1;i<=n;++i)
        scanf("%lld",&p[i]);

    if(n==1){cout<<0<<endl;return 0;}
    sum[1]=(2*p[1]+DFS(1,0))/(n-1);
    dfs(1,0);
    printf("%lld",a[1]);
    for(int i=2;i<=n;++i)
        printf(" %lld",a[i]);
    printf("\n");

    return 0;
}
阅读更多
版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_38786088/article/details/79961071
个人分类: 数学
上一篇牛客网练习赛15 C、吉姆的奇思妙想(数学单调性 + 二分法)
下一篇HUNNU oj 11757 Brackets (括号dp)
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

关闭
关闭