Sometimes Naive
Time Limit: 6000/3000 MS (Java/Others) Memory Limit: 65536/65536 K (Java/Others)Total Submission(s): 11 Accepted Submission(s): 6
Problem Description
Rhason Cheung had a naive problem, and asked Teacher Mai for help. But Teacher Mai thought this problem was too simple, sometimes naive. So she ask you for help.
She has a tree with n vertices, numbered from 1 to n . The weight of i -th node is wi .
You need to support two kinds of operations: modification and query.
For a modification operation u,w , you need to change the weight of u -th node into w .
For a query operation u,v , you should output ∑ni=1∑nj=1f(i,j) . If there is a vertex on the path from u to v and the path from i to j in the tree, f(i,j)=wiwj , otherwise f(i,j)=0 . The number can be large, so print the number modulo 109+7
She has a tree with n vertices, numbered from 1 to n . The weight of i -th node is wi .
You need to support two kinds of operations: modification and query.
For a modification operation u,w , you need to change the weight of u -th node into w .
For a query operation u,v , you should output ∑ni=1∑nj=1f(i,j) . If there is a vertex on the path from u to v and the path from i to j in the tree, f(i,j)=wiwj , otherwise f(i,j)=0 . The number can be large, so print the number modulo 109+7
Input
There are multiple test cases.
For each test case, the first line contains two numbers n,m(1≤n,m≤105) .
There are n numbers in the next line, the i -th means wi(0≤wi≤109) .
Next n−1 lines contain two numbers each, ui and vi , that means that there is an edge between ui and vi .
The following are m lines. Each line indicates an operation, and the format is " 1 u w "(modification) or " 2 u v "(query) (0≤w≤109)
For each test case, the first line contains two numbers n,m(1≤n,m≤105) .
There are n numbers in the next line, the i -th means wi(0≤wi≤109) .
Next n−1 lines contain two numbers each, ui and vi , that means that there is an edge between ui and vi .
The following are m lines. Each line indicates an operation, and the format is " 1 u w "(modification) or " 2 u v "(query) (0≤w≤109)
Output
For each test case, print the answer for each query operation.
Sample Input
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
Sample Output
341 348 612
Author
xudyh
Source
根据题意求所有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;
}