单7.27

liu_runda出的题,先%%%%%%%%%%%为敬

考试时只打了一个普通$n^2$暴力,然后$gauss$开的$long long$出现精度问题得了$0$分的好成绩

题解

$10\%$算法

暴力过$t==0$

$40\%$算法

gauss+暴力过前几个点

$100\%$算法

先算$t==0$

看$n^2$问题出现在那

重复计算多次距离,我们可以想个方法把自己的已经算过的存起来

现在假设我们有这样一棵树(1,2,3……代表点权,(为了方便)也代表编号)

看2 和4 的关系

子树内所有的点权值贡献$-1$,子树外所有点权贡献$+1$

事实上我们可以把我们计算过的存起来。用一个前缀和思想,把子树的和算出来,我们得到1的b就可以通过$b+ -$得到$2$的$b$

那么我们只需要一次dfs处理出所有子树权值和就可以得出来所有b

 式子$b[y]=b[x]-sum[y]+sum[1]-sum[y]$

这一点思想莫名像莫队,自从学了莫队我就觉得什么都是莫队

寿司这个题我就用了类似莫队思想,求出来一个$ans$然后通过$ans+-$,得到另一个$ans$

(我也颓了题解)

再看t==1的情况

看上去只能高斯消元,对吗?

实际上我们可以换种思路考虑

将$b[y]=b[x]-sum[y]+sum[1]-sum[y]$移项

得到$sum[1]-2\times sum[y]= b[y]-b[x]$

设dt数组表示两个sum之差dt[y]=b[y]-b[x]

我们可以用一次dfs求出dt那么,我们差的就只剩下sum[1] sum[y]了

仍然没法做对吗?

sum[1]其实可以求

假设1为根

$b[1]=\sum\limits_{i=2}^{n} sum[i]$

感性理解+手膜

还是这个图

每次都是路径长度$\times$权值,计算$2$的时候算了一遍$4 8 9$,计算4时又算了一遍$8 9$,路径每一个点上会被计算它到根节点之间节点个数(其实就是边数),所以最终得到的就是b[1]

同样,我们处理出来dt,再通过一次dfs求sum,然后最后dfs一次就好了

 代码

#include<bits/stdc++.h>
using namespace std;
#define mem(x) memset(x,0,sizeof(x))
#define ll long long
#define A 1100000
ll head[A],nxt[A],ver[A],a[A],b[A],sum[A],f[A],dt[A];
ll tot=0,n,t,xx,yy,task;
void add(ll x,ll y){
    nxt[++tot]=head[x],head[x]=tot,ver[tot]=y;
}
void re(){
    tot=0;
    mem(head);
    mem(nxt);
    mem(ver);
    mem(sum);
    mem(dt);
    mem(b);
    mem(f);
    mem(a);
}
void dfs1(ll x,ll fa){
    sum[x]=a[x];
    for(ll i=head[x];i;i=nxt[i]){
        ll y=ver[i];
        if(y==fa) continue;
        f[y]=x;
        dfs1(y,x);
        sum[x]+=sum[y];
    }
}
void dfs0(ll x,ll fa){
    for(ll i=head[x];i;i=nxt[i]){
        ll y=ver[i];
        if(y==fa) continue;
        b[y]=b[x]+sum[1]-2*sum[y];
        dfs0(y,x);
    }
}
void work1(){
    dfs1(1,0);
    for(ll i=2;i<=n;i++){
        b[1]+=sum[i];
    }
    dfs0(1,0);
    for(ll i=1;i<=n;i++){
        printf("%lld ",b[i]);
    }
    printf("\n");
}
void dfs2(ll x,ll fa){
    for(ll i=head[x];i;i=nxt[i]){
        ll y=ver[i];
        if(y==fa) continue;
        dfs2(y,x);
//        printf("b[]=%lld %lld\n",b[y],b[x]);
        dt[y]=b[y]-b[x];
    }
}
void dfs3(ll x,ll fa){
    for(ll i=head[x];i;i=nxt[i]){
        ll y=ver[i];
        if(y==fa) continue;
        dfs3(y,x);
        sum[y]=(sum[1]-dt[y])/2;
    }
}
void dfs4(ll x,ll fa){
    a[x]=sum[x];
    for(ll i=head[x];i;i=nxt[i]){
        ll y=ver[i];
        if(y==fa) continue;
        dfs4(y,x);
        a[x]-=sum[y];
    }
}
void work2(){
    dt[1]=0;
    ll zt=0;
    dfs2(1,0);
    for(ll i=2;i<=n;i++)
        zt+=dt[i];
    sum[1]=(zt+2*b[1])/(n-1);
//    printf("sum1=%lld\n",sum[1]);
    dfs3(1,0);
    dfs4(1,0);
    for(ll i=1;i<=n;i++){
        printf("%lld ",a[i]);
    }
    printf("\n");
}
int main(){
    scanf("%lld",&t);
    while(t--){
        re();
        scanf("%lld",&n);
        for(ll i=1;i<n;i++){
            scanf("%lld%lld",&xx,&yy);
            add(xx,yy),add(yy,xx);
        }
        scanf("%lld",&task);
        task++;
        if(task==1){
            for(ll i=1;i<=n;i++){
                scanf("%lld",&a[i]);
            }
            work1();
        }
        else{
            for(ll i=1;i<=n;i++){
                scanf("%lld",&b[i]);
            }
            work2();
        }
    }
}

猜你喜欢

转载自www.cnblogs.com/znsbc-13/p/11257689.html