← 回到所有文章

P9584 城市 题解

前言 感觉一些题解写的太麻烦了,这里提供一种更加简洁的做法。并且阅读本文请准备好草稿纸,树画出来一面大脑内存不够。 一些约定: 1. 本文拟定 1 号节点为根。 2. sizeu 表示以 u 节点为子…

题解文章的简约插画封面

前言

感觉一些题解写的太麻烦了,这里提供一种更加简洁的做法。并且阅读本文请准备好草稿纸,树画出来一面大脑内存不够。

一些约定:

  1. 本文拟定 11 号节点为根。

  2. sizeusize_u 表示以 uu 节点为子树的大小。

  3. sonuson_u 表示 uu 的儿子。

  4. parupar_u 表示 uu 的父亲。

  5. u→vu\rightarrow v 表示 uu 到 vv 节点边。

  6. wu→vw_{u\rightarrow v} 表示 uu 到 vv 节点边权。

正文

0x00 分析题目

相当于本文要求在添加一条边之后树上任意两点距离和,并且询问独立。

这样的题首先考虑计算出整个树本身不考虑其他点的答案贡献,然后每次询问时计算这个修改所造成的贡献即可。

0x01 计算推导

令 fuf_u 表示 uu 子树内到 uu 这个点的答案贡献;gug_u 表示 uu 子树外内到 vv 这个点的答案贡献。

显然整个树的贡献

baseans=∑u=1nfu+gubaseans=\sum_{u=1}^{n}f_u+g_u

每次询问的答案可以拆成,树原本的贡献和原本的点到加入点的贡,对于第二部分可以考虑加入的这条边的贡献和其他点到 kk 的贡献,这样可以快速计算。

ans=baseans+2×(fk+gk+n×w)ans=baseans+2\times\left(f_k+g_k+n\times w\right)

接下来考虑如何计算 fuf_u 和 gug_u。

对于 fuf_u 显然拆成其儿子的贡献 fvf_v 和 wu→vw_{u\rightarrow v} 的贡献。

fu=∑v∈sonufv+wu→v×sizevf_u=\sum_{v\in son_u} f_v+w_{u\rightarrow v}\times size_v

对于 gug_u 可以考虑拆成 gparug_{par_u} 和 parupar_u 子树内除开 uu 子树的贡献再加上 paru→upar_u\rightarrow u 边的贡献。发现第二部分就是 fparuf_{par_u} 减去计算 uu 时的贡献。那么:

gu=gparu+fparu−wparu→u×sizeug_u=g_{par_u}+f_{par_u}-w_{par_u\rightarrow u}\times size_u

0x02 代码实现

计算 ff 从下往上递推 DFS,计算 gg 从上往下递推 DFS 即可。

AC CODE

#include<bits/stdc++.h>
// #define ONLINE_JUDGE
#define INPUT_DATA_TYPE int
#define OUTPUT_DATA_TYPE int
INPUT_DATA_TYPE read(){register INPUT_DATA_TYPE x=0;register char f=0,c=getchar();while(c<'0'||'9'<c)f=(c=='-'),c=getchar();while('0'<=c&&c<='9')x=(x<<3)+(x<<1)+(c&15),c=getchar();return f?-x:x;}void print(OUTPUT_DATA_TYPE x){register char s[20];register int i=0;if(x<0){x=-x;putchar('-');}if(x==0){putchar('0');return;}while(x){s[i++]=x%10;x/=10;}while(i){putchar(s[--i]+'0');}return;}

struct EDGE{
    int to;
    long long w;
};

std::vector<EDGE> e[200010];

const long long mod=998244353;
long long f[200010],g[200010],siz[200010],n,baseans;

void addEdge(int u,int v,int w){
    e[u].push_back((EDGE){v,w});
    return;
}

void dfs(int u,int p){
    int v;
    siz[u]=1;
    for(auto edge:e[u])
        if((v=edge.to)!=p){
            dfs(v,u);
            f[u]=(f[u]+edge.w*siz[v]+f[v])%mod;
            siz[u]+=siz[v];
        }
    return;
}

void dfs2(int u,int p,long long w){
    if(u!=1)
        g[u]=(f[p]-(f[u]+w*siz[u])%mod+(n-siz[u])*w%mod+g[p]+mod)%mod;
    baseans=(baseans+g[u]+f[u])%mod;
    int v;
    for(auto edge:e[u])
        if((v=edge.to)!=p)
            dfs2(v,u,edge.w);
    return;
}

signed main(){
	#ifndef ONLINE_JUDGE
	freopen("name.in", "r", stdin);
	freopen("name.out", "w", stdout);
	#endif

    register int i,u,v,w;
    n=read();
    int q=read();
    
    for(i=1;i<n;++i){
        u=read();
        v=read();
        w=read();
        addEdge(u,v,w);
        addEdge(v,u,w);
    }

    dfs(1,1);
    dfs2(1,1,0);

    for(i=0;i<q;++i){
        u=read();
        w=read();
        print((baseans+((g[u]+f[u]+n*w)<<1))%mod);puts("");
    }

	#ifndef ONLINE_JUDGE
	fclose(stdin);
	fclose(stdout);
	#endif
    return 0;
}

谢谢你,读到这里。

本文同步自洛谷专栏,保留原始正文与发布时间。在洛谷查看原文 ↗

搜索文章

输入关键词,发现一些值得阅读的文字。

探索文字里的小小世界esc 关闭