题意:
给定一棵nnn个点,n−1n-1n−1条边的带边权的无向图,图上的点可以长蘑菇。克拉莉丝起始点在点111。
接着发生qqq次事件。事件有两种:
- 1 v x1\;v\;x1vx表示在点vvv上新长了xxx个蘑菇。
- 2 v2\;v2v表示克拉莉丝的起始位置变成了点vvv。
在每个事件后,克拉莉丝想要知道她收集完所有蘑菇的代价。
对于任意一个蘑菇,收集它的代价是这个蘑菇所在点到克拉莉丝起始点路径上最靠近克拉莉丝起始点的边的边权。
数据范围:1≤n≤1061\leq n\leq 10^{6}1≤n≤106,1≤q≤1061\leq q\leq 10^{6}1≤q≤106
思路:
我们把起点的代价贡献分为三类求,连父亲的边,连重儿子的边,连轻儿子的边。
考虑其中某个点作为起点,与父亲连的边的贡献次数是除了这个点子树以外其他点的蘑菇总数。这个我们只需要能处理子树和查询(dfs序+树状数组)就行了,然后贡献次数就是根的子树和减去这个点的子树和。
然后考虑重儿子的边的贡献次数,就是重儿子的子树和。
最后是轻儿子的贡献,由于轻儿子比较多,我们没办法去枚举。我们考虑在一个轻儿子上加蘑菇时,就暴力跳轻边,把贡献加在跳到的每个点上。这样跳的次数是lognlognlogn的。
最后复杂度就是o(nlogn)o(nlogn)o(nlogn)。
代码:
改天再写。
(upd:修复了轻边跳到重边为止的bug,对于重链我们维护一个top数组记链头,可以直接跳过重链)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 1000000;
ll c[1000005];
void add(ll x,ll k) {
while(x<=N) {
c[x]+=k;
x+=x&-x;
}
}
ll getSum(ll x) {
ll res=0;
while(x>0) {
res+=c[x];
x-=x&-x;
} return res;
}
ll val[1000005],sum[1000005];
ll fa[1000005],siz[1000005],son[1000005];
vector<ll>v[1000005],g[1000005];
ll id[1000005],idd;
void dfs(ll x,ll pre)
{
id[x]=++idd;
fa[x]=pre;
siz[x]=1;
for(ll i=0;i<v[x].size();i++)
{
ll to=v[x][i],w=g[x][i];
if(to!=pre)
{
dfs(to,x);
val[to]=w;
siz[x]+=siz[to];
}
}
for(ll to:v[x])if(to!=pre)
{
if(son[x]==0||siz[to]>siz[son[x]])son[x]=to;
}
}
ll top[1000005];
void dfstop(ll x,ll pre)
{
if(son[pre]==x)top[x]=top[pre];
else top[x]=x;
for(ll i=0;i<v[x].size();i++)
{
ll to=v[x][i];
if(to!=pre)
{
dfstop(to,x);
}
}
}
int main()
{
ll n;
scanf("%lld",&n);
for(ll i=1;i<n;i++)
{
ll a,b,w;
scanf("%lld%lld%lld",&a,&b,&w);
v[a].push_back(b);
v[b].push_back(a);
g[a].push_back(w);
g[b].push_back(w);
}
dfs(1,0);
dfstop(1,0);
ll q;
scanf("%lld",&q);
int now=1;
while(q--)
{
ll opt;
scanf("%lld",&opt);
if(opt==1)
{
ll v,x;
scanf("%lld%lld",&v,&x);
add(id[v],x);
while(v!=0)
{
sum[fa[top[v]]]+=x*val[v];
v=fa[top[v]];
}
}
else
{
ll v;
scanf("%lld",&v);
now=v;
}
ll ans=val[now]*(getSum(n)-(getSum(id[now]+siz[now]-1)-getSum(id[now]-1)))+val[son[now]]*(getSum(id[son[now]]+siz[son[now]]-1)-getSum(id[son[now]]-1))+sum[now];
printf("%lld\n",ans);
}
return 0;
}
/*
6
1 2 1
1 4 1
2 3 1
4 5 1
5 6 1
1
1 3 1
*/