P1043 策划道路 官方题解
Gioush OJ · P1043 策划道路
策划道路
【题意简述】
给出一棵 个结点的带权树。每个方案独立地新增结点 ,并用权值为 的边连接到原结点 ,求新树中全部有序点对的距离和。
【Hint】
【提示】
设原树全部有序点对距离和为 ,结点 到全部原结点的距离和为 。方案答案为 ,只需换根求出全部 。
【数据点 1∼31\sim 31∼3】
对每份方案直接建出新树,再从每个结点出发搜索。时间复杂度为 。
【数据点 4∼74\sim 74∼7】
记原树全部有序点对距离和为 ,并定义
新结点到原结点 的距离为 ,反向距离相同,因此一份方案的答案为
固定根后,边 两侧分别有 与 个结点,其对 的贡献为
这一档可以预处理 ,再对每个方案搜索求 。
【正解】
任选结点 为根。令 表示 的子树大小,令 表示 到其子树内全部结点的距离和。若 枚举 的儿子,则
所以 。
接下来换根。设 是 的儿子,边权为 。根从 移到 后, 子树内的 个结点距离均减少 ,其余结点距离均增加 ,所以
第二次搜索即可求出全部 。又因为每个有序点对 恰好在 中出现一次,原树距离和为
最后对每份方案输出 。
【复杂度分析】
预处理时间复杂度为 ,每次询问为 ,总时间复杂度为 ,空间复杂度为 。
【参考代码】
#include <cstdio>#include <cstring>#define MAXN 200010const int Mod=998244353;using namespace std;int Calc(int a,int b){return (a+=b)<Mod?a:a-Mod;}int Mul(int a,int b){return 1ll*a*b%Mod;}int Del(int a,int b){return (a-=b)>0?a:a+Mod;}int Total=0,Head[MAXN],Count[MAXN],Root=1,Size[MAXN],n;int Sum=0;struct E{ int St,Ed; int Next; int Value;}Edge[MAXN<<1];void Edge_Add(int St,int Ed,int Value){ Total++; Edge[Total].St=St; Edge[Total].Ed=Ed; Edge[Total].Value=Value; Edge[Total].Next=Head[St]; Head[St]=Total;}void DFS_F(int Now,int From,int Value){ Size[Now]=1; for(int i=Head[Now];i;i=Edge[i].Next){ int To=Edge[i].Ed; if(To==From) continue; DFS_F(To,Now,Calc(Value,Edge[i].Value)); Size[Now]+=Size[To]; Count[Root]=Calc(Count[Root],Calc(Value,Edge[i].Value)); }}void DFS_Pass(int Now,int From){ for(int i=Head[Now];i;i=Edge[i].Next){ int To=Edge[i].Ed; if(To==From) continue; Count[To]=Calc(Count[Now],Mul((n-Size[To]-Size[To]),Edge[i].Value)); Count[To]=(Count[To]%Mod+Mod)%Mod; DFS_Pass(To,Now); }}void Build(int Line,int Cost){ printf("%d\n",Calc(Sum,Mul(2,Calc(Count[Line],Mul(Cost,n)))));}void Solve(){ int Q; scanf("%d%d",&n,&Q); memset(Head,0,sizeof(int)*(n+2)); memset(Count,0,sizeof(int)*(n+2)); memset(Size,0,sizeof(int)*(n+2)); Total=Sum=0;Root=1; int In1,In2,In3; for(int i=1;i<=n-1;i++){ scanf("%d%d%d",&In1,&In2,&In3); Edge_Add(In1,In2,In3); Edge_Add(In2,In1,In3); } DFS_F(Root,Root,0); DFS_Pass(Root,Root); for(int i=1;i<=n;i++) Sum=Calc(Sum,Count[i]); while(Q--){ scanf("%d%d",&In1,&In2); Build(In1,In2); }}int main(){ int c,T; scanf("%d%d",&c,&T); while(T-->0) Solve(); return 0;}