魔法水晶承载着魔法师的法力,是魔法师法力的结晶。
Elsa 拥有 n 个魔法水晶。为了让这 n 个魔法水晶处于相互联系的状态中,
并且不出现流动混乱,Elsa 用 n-1 条法力流动通道将魔法水晶联系起来。每条
通道直接连接两个魔法水晶,并且每对魔法水晶都直接或间接相连。
每条法力流动通道有一个延迟量,一对魔法水晶之间的延迟量是连接它们
的路径上所有通道的延迟量之和。n 个魔法水晶这一整体的总延迟是每对魔法
水晶之间的延迟量的和。
Elsa 会进行 m 次施法操作,每次施法会将连接魔法水晶 u 和 v 的路径上所
有的法力流动通道的延迟量增加 w。
她需要你帮助她动态地计算 n 个魔法水晶这一整体的总延迟。
【输入格式】
第一行一个数 n,接下来 n-1 行,每行三个数 a、b、c,表示有一条连接
a、b 的延迟量为 c 的法力流动通道。
接下来一个数 m,表示施法次数。接下来 m 行,每行三个数 u、v、w,表示
连接 u 和 v 的路径上的每一条法力流动通道的延迟量都增加 w。
【输出格式】
输出 m+1 行,第 i 行输出一个整数表示完成前(i-1)次施法操作后的总延
迟。
答案对 1,000,000,007 取模。
【样例输入】
3
1 2 2
1 3 1
2
1 2 -2
2 3 1
【样例输出】
6
2
6
【数据规模与约定】
20%的数据,n,m≤50
40%的数据,n,m≤300
60%的数据,n,m≤3000
另 20%的数据,m=0
100%的数据,n,m≤100000,-10^9≤c,w≤10^9
每条边的贡献单独计算。=左侧节点数×右侧节点数×边权
若一条边两侧分别有a个点和b个点,那么这条边会出现在ab个点
对中。
这样就可以计算距离和了。
修改,可以先求出路径上每条边的ab和,再乘上变化量,就是距离
和的变化量了。
倍增优化。
#include
#include
#include
#define LL long long
using namespace std;
int next[200003],point[100003];//next数组存储,next的下标表示边的编号,存的是以该边的起点为起点的上一条边的编号;point的下标是点的编号,存的是以该点为起点的最后一条边的编号
LL val[200003],c;//val的下标是边的编号,存储边的权值
int behind[200003];//behind的下标是边的编号,存的是边的终点
int f[100003][20],h[100003],size[100003];//f的第一维表示当前节点号,第二维表示向上2^i条边,存储所到达的节点号;h表示节点深度;size表示一侧子树的节点数
LL lensum[100003][20];//lensum的第一维表示当前节点号,第二维表示向上2^i条边
存储所经过的所有边的贡献之和(不乘边权,只计算经过的次数即左侧的节点数乘右侧的节点数)
long long n,m,ans,i,j,a,b;
int mi[20],p;
void add(int x,int y,long long v,int num)//next数组双向存储
{
behind[num]=y;
next[num]=point[x];
point[x]=num;
val[num]=v;
}
void dfs(int x,int ahead)
{
size[x]=1;
int now=point[x];
while (now)//枚举与当前节点直接相连的节点
{
if (behind[now]!=ahead)
{
dfs(behind[now],x);//递归计算子树节点数
size[x]+=size[behind[now]];该点一侧的节点数等于他所有子树的节点数相加
ans=(ans+(LL)size[behind[now]]%p*(LL)(n-size[behind[now]])%p*val[now])%p; //答案加上当前点到与他直接相连的节点的边的贡献
}
now=next[now];
}
}
void build (int x,int ahead,int dep)//构造倍增,向上2^0,2^1,2^2,2^3……
{
h[x]=dep;
for (int i=1;i<16;i++)
{
if (h[x]-mi[i]<0) break;
f[x][i]=f[f[x][i-1]][i-1];向上2^i相当于向上2^i-1到达某个点再由这个点向上2^i-1
lensum[x][i]=(lensum[x][i-1]+lensum[f[x][i-1]][i-1])%p;
}
int now=point[x];
while (now)
{
if (behind[now]!=ahead)//即将遍历的点不是来的时候经过的点
{
f[behind[now]][0]=x;//与x的后继节点相距2的0次方即1的节点是x本身
lensum[behind[now]][0]=(LL)size[behind[now]]*(LL)(n-size[behind[now]])%p;
build(behind[now],x,dep+1);
}
now=next[now];
}
}
long long lca(int x,int y)//求x,y到他们的最近公共祖先需要经过的边数的贡献值总和
{
if (h[x]
swap(x,y);
int depcha=h[x]-h[y];
long long sum=0;
for (int i=0;i<17;i++)//使X,Y先走到同一深度
if (depcha>>i&1)//右移i位判断最后一位是否为1,既判段是否可以减去2^i,将他分成几个2的几次方相加(2 在二进制下为10,4在二进制下为100,8在二进制下为1000,所以差值可以按位分解,若为1则减去2^i,直到减到0为止)
{
sum=(sum+lensum[x][i])%p;
x=f[x][i];//向上2^i到达的节点
}
if (x==y)
return sum;
for (int i=16;i>=0;i--)
if (f[x][i]!=f[y][i])//从大到小保证不会跳过最近公共祖先
{
sum=(sum+lensum[x][i]+lensum[y][i])%p;
x=f[x][i];
y=f[y][i];
}
sum=(sum+lensum[x][0]+lensum[y][0])%p;
return sum;
}
int main()
{
freopen("sum.in","r",stdin);
freopen("sum.out","w",stdout);
scanf("%d",&n);
mi[0]=1;
p=1000000007;
for (i=1;i<17;i++)
mi[i]=mi[i-1]*2;
for (i=1;i<=n-1;i++)
{
scanf("%d%d%I64d",&a,&b,&c);
if (c<0)
c+=p;
add(a,b,c,i*2);
add(b,a,c,i*2+1);
}
dfs(1,0);
printf("%I64d\n",ans);
build(1,0,1);
scanf("%d",&m);
for (i=1;i<=m;i++)
{
scanf("%d%d%I64d",&a,&b,&c);
c=(c+p)%p;
int k=lca(a,b);//k即为a,b之间所有边经过的次数之和
ans=(ans+c*k)%p;
printf("%I64d\n",ans);
}
return 0;
}