6 5 1 2 3 4 5 6 1 2 1 3 2 4 2 5 4 6 2 3 5 1 5 6 2 2 3 1 1 7 2 2 4
341 348 612
根据题意求所有i,j路径经过路径u,v上的点,那么对wi*wj累加
反面考虑:所有路径累加和 - 不经过路径的累加和
那么只需快速得到不在路径上子树的规模,他们的平方的累加和就是结果。
解题报告用的是树链剖分。我还没想好怎么实现。
比赛的时候想到LCT了,但是之前没写过用子树标记父亲的标记的代码,所以最后也没打出来。
但是之前看过别人有实现过,晚上想一想也想到了。
如何实现LCT的结点记录自己子树的信息:
用一个标记如S1,记录,这个标记只有在断开连接或者建立连接的时候才会更新。然后用一个S2,这个标记是用于splay上做更新的
于是s1的更改只需在access操作更改,而s2只需update时更新即可。
如果要用我的代码当模板注意我的注释哦。
#include<cstdio> #include<cstring> #include<algorithm> #include<iostream> #include<vector> using namespace std; #define maxn 200007 #define inf 1000000000 #define ll long long ll mod = 1000000007; struct Node{ Node *fa,*ch[2]; bool rev,root;//基本结构不需要修改 ll val,s1,s2,s3,s4;//这里的标记可以更改 }; Node pool[maxn]; Node *nil,*tree[maxn]; int cnt = 0; void init(){ cnt = 1; nil = tree[0] = pool; nil->ch[0] = nil->ch[1] = nil; nil->s1 = nil->s2 = nil->val = nil->s3 = nil->s4 = 0; } Node *newnode(ll val,Node *f){ pool[cnt].fa = f; pool[cnt].ch[0]=pool[cnt].ch[1]=nil; pool[cnt].rev = false; pool[cnt].root = true;//以下可修改 pool[cnt].val = val; pool[cnt].s1 = 0; pool[cnt].s2 = 0; pool[cnt].s3 = 0; pool[cnt].s4 = 0; return &pool[cnt++]; } //左右子树反转******真正把结点变为根 void update_rev(Node *x){ if(x == nil) return ; x->rev = !x->rev; swap(x->ch[0],x->ch[1]); } //splay向上更新信息****** void update(Node *x){//splay需要自己写更新代码 if(x == nil) return ; x->s2 = x->s1 + x->val; x->s4 = x->s3; if(x->ch[0] != nil){ x->s2 += x->ch[0]->s2; if(x->s2 >= mod) x->s2 -= mod; x->s4 += x->ch[0]->s4; x->s4 %= mod; } if(x->ch[1] != nil){ x->s2 += x->ch[1]->s2; if(x->s2 >= mod) x->s2 -= mod; x->s4 += x->ch[1]->s4; x->s4 %= mod; } } //splay下推信息****** void pushdown(Node *x){//树的反转,才能保证把一个结点提为根。如果有其他下推操作,自己加入 if(x->rev != false){ update_rev(x->ch[0]); update_rev(x->ch[1]); x->rev = false; } } //splay在root-->x的路径下推信息****** void push(Node *x){//不需要改 if(!x->root) push(x->fa); pushdown(x); } //将结点x旋转至splay中父亲的位置****** void rotate(Node *x){//旋转操作,不太需要改,除非有懒标记,需要加上pushdown,update Node *f = x->fa, *ff = f->fa; int t = (f->ch[1] == x); if(f->root) x->root = true, f->root = false; else ff->ch[ff->ch[1] == f] = x; x->fa = ff; f->ch[t] = x->ch[t^1]; x->ch[t^1]->fa = f; x->ch[t^1] = f; f->fa = x; update(f); } //将结点x旋转至x所在splay的根位置****** void splay(Node *x){//不需要改 push(x); Node *f, *ff; while(!x->root){ f = x->fa,ff = f->fa; if(!f->root) if((ff->ch[1]==f)&&(f->ch[1] == x)) rotate(f); else rotate(x); rotate(x); } update(x); } //将x到树根的路径并成一条path****** Node *access(Node *x){ Node *y = nil,*z; while(x != nil){ splay(x); z = x->ch[1];//记录断开的结点 x->ch[1]->root = true; (x->ch[1] = y)->root = false; update(z); x->s1 += z->s2;//更新父亲信息 x->s3 += (z->s2*z->s2); x->s1 %= mod; x->s3 %= mod; x->s1 -= y->s2; x->s3 -= y->s2*y->s2; x->s1 %= mod; x->s3 %= mod; update(x); y = x; x = x->fa; } return y; } //将结点x变成树根****** void be_root(Node *x){//基本操作 access(x); splay(x); update_rev(x); } int value[maxn]; vector<int> head[maxn]; void dfs(int u,int f){//dfs建立没有链的森林 tree[u]->fa = tree[f]; for(int i = 0;i < head[u].size() ;i++){ int v = head[u][i]; if(v == f) continue; dfs(v,u); tree[u]->s1 += tree[v]->s2; tree[u]->s1 %= mod; tree[u]->s3 += tree[v]->s2*tree[v]->s2; tree[u]->s3 %= mod; } update(tree[u]); } int main(){ int n,m; while(scanf("%d%d",&n,&m)!=EOF){ for(int i = 1;i <= n; i++) scanf("%d",&value[i]); for(int i = 0;i <= n; i++) head[i].clear(); int u,v; for(int i = 1;i < n; i++){ scanf("%d%d",&v,&u); head[v].push_back(u); head[u].push_back(v); } init(); for(int i=1;i <= n;i++) tree[i] = newnode(value[i],nil); dfs(1,0); ll total = 0; for(int i = 1;i <= n; i++) total += value[i]; total %= mod; int t; for(int i = 0;i < m; i++){ scanf("%d%d%d",&t,&u,&v); if(t == 1){ be_root(tree[u]); access(tree[u]); total = total + v - tree[u]->val; total %= mod; tree[u]->val = v; update(tree[u]); } else { be_root(tree[u]); access(tree[v]); splay(tree[v]); ll ans = total*total%mod-tree[v]->s4; ans = (ans%mod+mod)%mod; printf("%I64d\n",ans); } } } return 0; }