魔法水晶

来源:互联网 发布:一个人开了20家淘宝店 编辑:程序博客网 时间:2024/04/30 03:03

魔法水晶

魔法水晶承载着魔法师的法力,是魔法师法力的结晶。

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;

}


0 0