1 条题解
-
0
考虑拆贡献。
我们考虑用一个点来表示一条边,假设一条边连接 且 是 的父节点,那么我们可以用点 来代表这条边,容易证明每条边都存在唯一的点来代表它。
所以我们考虑计算有多少条简单路径经过这条边即可,显然对于 对应的边来说,有 条简单路径经过它,那么我们就需要把答案更新为 。
现在考虑修改操作,显然只需要维护一下增量即可,那增量可以写成:
$$\Delta=z\cdot \sum_{x\in u\to v 的路径上}siz_x\cdot(n-siz_x) $$只需要求出后面一坨 里的数即可,显然树上前缀和可做,时间复杂度 。
不要忘记把答案更新为 。
#include <iostream> #include <vector> #include <algorithm> #define ll long long using namespace std; const ll N=1e5+10; const ll MOD=1e9+7; ll n,fa[N][30],siz[N],dat[N],sum[N],dep[N]; ll ans; vector<ll> e[N],quan[N]; void dfs(ll u,ll f,ll d){ siz[u]=1; ll lz=e[u].size(); for(ll i=0;i<lz;i++){ ll v=e[u][i]; if(v==f) continue; dfs(v,u,quan[u][i]); siz[u]+=siz[v]; } dat[u]=siz[u]*(n-siz[u])%MOD; ans=(ans+dat[u]*d%MOD+MOD)%MOD; } void dfs2(ll u,ll f){ dep[u]=dep[f]+1; fa[u][0]=f; for(ll i=1;i<30;i++) fa[u][i]=fa[fa[u][i-1]][i-1]; sum[u]=(sum[f]+dat[u])%MOD; ll lz=e[u].size(); for(ll i=0;i<lz;i++){ ll v=e[u][i]; if(v==f) continue; dfs2(v,u); } } ll lca(ll x,ll y){ if(dep[x]<dep[y]) swap(x,y); for(ll i=29;i>=0;i--){ if(dep[fa[x][i]]>=dep[y]){ x=fa[x][i]; } } if(x==y) return x; for(ll i=29;i>=0;i--){ if(fa[x][i]!=fa[y][i]){ x=fa[x][i]; y=fa[y][i]; } } return fa[x][0]; } int main(){ ios::sync_with_stdio(false); cin.tie(0),cout.tie(0); cin>>n; for(ll i=1;i<n;i++){ ll u,v,w; cin>>u>>v>>w; e[u].push_back(v); e[v].push_back(u); quan[u].push_back(w); quan[v].push_back(w); } dfs(1,0,0); dfs2(1,0); cout<<ans<<"\n"; ll q; cin>>q; while(q--){ ll L,R,z,minu; cin>>L>>R>>z; minu=sum[L]+sum[R]-2*sum[lca(L,R)]; minu%=MOD; if(minu<0) minu+=MOD; ans=(ans+minu*z%MOD+MOD)%MOD; cout<<ans<<"\n"; } return 0; }
- 1
信息
- ID
- 12
- 时间
- 1000ms
- 内存
- 256MiB
- 难度
- 10
- 标签
- (无)
- 递交数
- 2
- 已通过
- 2
- 上传者