传送门:https://acm.ecnu.edu.cn/contest/105/problem/Q/

一棵树,支持两种操作:给一条路径上的节点加上一个等差数列;求两点路径上节点和.

很明显,熟练剖分.用线段树维护链上的区间和,每个节点中记录等差数列的首项,公差和区间和.因为两个等差数列叠加之后还是等差数列,所以将首项与公差视作懒惰标记.

因为在寻找LCA的过程中,u往上跳的时候,其实是要维护递减的等差数列(dfs序是u->topu递减,而数列是u->topu递增);v往上跳的时候,是递增的.计算出u->v上路径的长度,就可以根据等差数列通项公式求出an.

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
#define lson rt<<1
#define rson rt<<1|1
#define Lson l,m,lson
#define Rson m+1,r,rson
using namespace std;
typedef long long LL;
const int mod = 1e9+7;
const int maxn =5e4+5;
struct Edge{
int to,next;
}E[maxn<<1];
int n,head[maxn],tot;
int cnt,idx,siz[maxn],fa[maxn],son[maxn],dep[maxn],top[maxn],id[maxn],rnk[maxn];
int a[maxn];
struct Node{
LL a1,d,sum; //区间首项,公差,区间和
}tree[maxn<<2]; LL qpow(LL a,LL N)
{
LL res=1;
while(N){
if(N&1) res = res*a %mod;
a = a*a%mod;
N>>=1;
}
return res;
} const LL rev2 = qpow(2,mod-2); //2逆元 void init()
{
cnt=idx=tot=0;
memset(head,-1,sizeof(head));
dep[1]=0,fa[1]=1,siz[0]=0;
memset(son,0,sizeof(son));
} void AddEdge(int u,int v)
{
E[tot] = (Edge){v,head[u]};
head[u]=tot++;
} void dfs1(int u)
{
siz[u]=1;
for(int i=head[u];~i;i=E[i].next){
int v=E[i].to;
if(v!=fa[u]){
fa[v]=u;
dep[v]=dep[u]+1;
dfs1(v);
siz[u]+=siz[v];
if(siz[son[u]]<siz[v]) son[u]=v;
}
}
} void dfs2(int u,int topu)
{
top[u]= topu;
id[u] = ++idx;
rnk[idx] = u;
if(!son[u]) return;
dfs2(son[u],top[u]);
for(int i=head[u];~i;i=E[i].next){
int v=E[i].to;
if(v!=fa[u]&&v!=son[u]) dfs2(v,v);
}
} //------------------------------------线段树 void pushup(int rt){
tree[rt].sum = (tree[lson].sum + tree[rson].sum)%mod;
} void pushdown(int l,int r,int rt){
if(tree[rt].a1 !=0 || tree[rt].d !=0){
LL a1 = tree[rt].a1, d = (tree[rt].d+mod)%mod;
int m = (l+r)>>1;
int n1 = m-l+1, n2 = r-m;
LL f1 = a1%mod, f2 = (a1 + n1*d%mod +mod)%mod;
tree[lson].a1 = (tree[lson].a1+f1)%mod;
tree[rson].a1 = (tree[rson].a1+f2)%mod;
tree[lson].d = (tree[lson].d+d+mod)%mod;
tree[rson].d = (tree[rson].d+d+mod)%mod;
tree[lson].sum = (tree[lson].sum+ f1*n1 %mod+
n1*(n1-1)%mod *d %mod*rev2 %mod + mod)%mod;
tree[rson].sum = (tree[rson].sum+ f2*n2 %mod+
n2*(n2-1)%mod *d %mod *rev2 %mod +mod)%mod;
//cout<<(lson)<<":"<<tree[lson].sum<<" "<<(rson)<<":"<<tree[rson].sum<<endl;
tree[rt].a1 = tree[rt].d = 0;
}
} void build(int l,int r,int rt)
{
tree[rt].a1 = tree[rt].d =0;
if(l==r){
tree[rt].sum = 0;
return;
}
int m = (l+r)>>1;
build(Lson);
build(Rson);
pushup(rt);
} void update(int L,int R,LL a1,LL v,int l=1,int r=n,int rt=1){
if(L<=l && R>=r){
int nn = r-l+1;
v = (v+mod)%mod;
LL f1 = (a1 + (l-L)*v %mod +mod)%mod; //叠加的等差数列首项
tree[rt].a1 = (tree[rt].a1+f1)%mod;
tree[rt].d = (tree[rt].d+v+mod)%mod; //公差
tree[rt].sum = (tree[rt].sum+f1*nn %mod +nn*(nn-1)%mod
*v %mod*rev2%mod + mod)%mod;
return;
}
pushdown(l,r,rt);
int m =(l+r)>>1;
if(L<=m) update(L,R,a1,v,Lson); //首项对齐
if(R>m) update(L,R,a1,v,Rson); //首项对齐
pushup(rt);
} LL query(int L,int R,int l=1,int r=n,int rt=1){ //区间查询
if(L<=l && R>=r) return tree[rt].sum;
pushdown(l,r,rt);
int m = (l+r)>>1,ans=0;
LL res=0;
if(L<=m) res = (res+query(L,R,Lson)+mod)%mod;
if(R>m) res= (res+query(L,R,Rson)+mod)%mod;
pushup(rt);
return res;
} //----------------------------------树剖
void debug()
{
for(int i=1;i<=n;++i) printf("%lld ",query(id[i],id[i]));
cout<<endl;
} int getdist(int u,int v)
{ //计算两点之间的路径长度
int ans=0;
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
ans += id[u] - id[top[u]]+1;
u = fa[top[u]];
}
if(dep[u]>dep[v]) swap(u,v);
ans += id[v]-id[u];
return ans;
} void UPDATE(int u,int v,LL a1, LL w)
{
int nn = getdist(u,v);
LL an = (a1 + nn*w)%mod;
//cout<<nn<<" "<<a1<<" "<<an<<endl;
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]){
int cnt = id[v] - id[top[v]]+1;
update(id[top[v]],id[v], (an-(cnt-1)*w %mod+mod)%mod,w); //递增
an = (an - (cnt*w%mod) +mod)%mod;
v = fa[top[v]];
}
else{
int cnt = id[u] - id[top[u]] + 1;
update(id[top[u]],id[u],(a1+(cnt-1)*w)%mod,(mod-w)%mod); //反向
a1 = (a1+ cnt*w %mod)%mod;
u = fa[top[u]];
}
//cout<<a1<<" "<<an<<endl;
}
if(dep[u]<dep[v]){
int cnt = id[v] - id[u]+1;
update(id[u],id[v],an-(cnt-1)*w,w); //递增
}
else{
int cnt = id[u] - id[v] +1;
update(id[v],id[u],a1+(cnt-1)*w,-w); //反向
}
} LL Qsum(int u,int v)
{
int ans=0;
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]) swap(u,v);
ans = (ans+query(id[top[u]],id[u])) %mod;
u = fa[top[u]];
}
if(dep[u]>dep[v]) swap(u,v);
ans = (ans + query(id[u],id[v]))%mod;
return ans;
} int main()
{
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
freopen("out.txt","w",stdout);
#endif
int m,q,u,v,op;
while(scanf("%d%d",&n,&q)==2){
init();
for(int i=1;i<n;++i){
scanf("%d%d",&u,&v); u++,v++;
AddEdge(u,v);
AddEdge(v,u);
}
dfs1(1);
dfs2(1,1);
build(1,n,1);
LL w;
while(q--){
scanf("%d",&op);
if(op==1){
scanf("%d %d %lld",&u,&v,&w); u++, v++;
if(w==0) continue;
UPDATE(u,v,w,w);
//debug();
}
else{
scanf("%d %d",&u,&v); u++, v++;
printf("%lld\n",Qsum(u,v));
}
}
}
return 0;
}

2018.9 ECNU ICPC/CCPC Trial Round #2 Query On Tree (树链剖分+线段树维护)的更多相关文章

  1. ACM-ICPC 2018 焦作赛区网络预赛 E Jiu Yuan Wants to Eat (树链剖分+线段树)

    题目链接:https://nanti.jisuanke.com/t/31714 题意:给你一棵树,初始全为0,有四种操作: 1.u-v乘x    2.u-v加x   3. u-v取反  4.询问u-v ...

  2. Educational Codeforces Round 3 E. Minimum spanning tree for each edge 最小生成树+树链剖分+线段树

    E. Minimum spanning tree for each edge time limit per test 2 seconds memory limit per test 256 megab ...

  3. Codeforces Round #200 (Div. 1) D. Water Tree 树链剖分+线段树

    D. Water Tree time limit per test 4 seconds memory limit per test 256 megabytes input standard input ...

  4. 2018牛客网暑假ACM多校训练赛(第七场)I Tree Subset Diameter 动态规划 长链剖分 线段树

    原文链接https://www.cnblogs.com/zhouzhendong/p/NowCoder-2018-Summer-Round7-I.html 题目传送门 -  https://www.n ...

  5. 2019年ICPC南昌网络赛 J. Distance on the tree 树链剖分+主席树

    边权转点权,每次遍历到下一个点,把走个这条边的权值加入主席树中即可. #include<iostream> #include<algorithm> #include<st ...

  6. 从lca到树链剖分 bestcoder round#45 1003

    bestcoder round#45 1003 题,给定两个点,要我们求这两个点的树上路径所经过的点的权值是否出现过奇数次.如果是一般人,那么就是用lca求树上路径,然后判断是否出现过奇数次(用异或) ...

  7. [集训队作业2018]蜀道难——TopTree+贪心+树链剖分+链分治+树形DP

    题目链接: [集训队作业2018]蜀道难 题目大意:给出一棵$n$个节点的树,要求给每个点赋一个$1\sim n$之内的权值使所有点的权值是$1\sim n$的一个排列,定义一条边的权值为两端点权值差 ...

  8. Codeforces Round #620 F2. Animal Observation (hard version) (dp + 线段树)

    Codeforces Round #620 F2. Animal Observation (hard version) (dp + 线段树) 题目链接 题意 给定一个nm的矩阵,每行取2k的矩阵,求总 ...

  9. [Codeforces Round #254 div1] C.DZY Loves Colors 【线段树】

    题目链接:CF Round #254 div1 C 题目分析 这道题目是要实现区间赋值的操作,同时还要根据区间中原先的值修改区间上的属性权值. 如果直接使用普通的线段树区间赋值的方法,当一个节点表示的 ...

随机推荐

  1. c# 扩展方法 奇思妙用 高级篇 九:OrderBy(string propertyName, bool desc)

    下面是 Queryable 类 中最常用的两个排序的扩展方法: 1 2 public static IOrderedQueryable<TSource> OrderBy<TSourc ...

  2. Spring boot Junit Test单元测试

    Spring boot 1.40 JUnit 4 需要依赖包 spring-boot-starter-test.spring-test 建立class,加上如下注解,即可进行单元测试,别的帖子里说要加 ...

  3. 工作流JBPM_day01:1-说明_MyProcessDesigner_流程设计器

    工作流JBPM_day01:1-说明 先只做请假功能,怎么做? (请假可以和考勤整合到一起) 1,银行(拿号---叫号---办理) 2,餐馆(点菜---上菜---结账) 3,网购(下订单--配送--收 ...

  4. WPF进阶之接口(1):IValueConverter,IMultiValueConverter

    看一个例子,FontFamily="Trebuchet MS, GlobalSansSerif.CompositeFont" .这样一条简单的语句,熟悉WPF的人在xaml中可能经 ...

  5. swift - SQLite数据库的使用(引用)

    SQLite轻量级数据库在移动应用中使用非常普遍,但是目前的库是C编写的,为了方便使用,对SQLite相关的操作用Swift进行了封装.这个封装代码使用了一个开源项目SQLiteDB,地址是:http ...

  6. JZOJ.5288【NOIP2017模拟8.17】球场大佬

    Description       每天下午,古猴都会去打羽毛球.但是古猴实在是太强了,他必须要到一些比较强的场去打.但是每个羽毛球场都有许多的人排着队,每次都只能上四个人,每个人都有自己的能力值,然 ...

  7. powerdesigner 导入SQL脚本生成模型

  8. 160323、理解Java虚拟机体系结构

    今天看到一篇文章,觉得写得不错,特拿来跟大家分享一下 1 概述 众所周知,Java支持平台无关性.安全性和网络移动性.而Java平台由Java虚拟机和Java核心类所构成,它为纯Java程序提供了统一 ...

  9. Eclipse+ADT+Android SDK 搭建安卓开发环境(版权属于forever-z)

    运行环境 windows 7或者10(64位); 为例eclipse-jee-neon-3-win32-x86_64: ADT-23.0.4 下载地址 安装JDK 这里可以参考关于安装JDK的教程,请 ...

  10. Struts2+Spring3+MyBatis3整合以及Spring注解开发

     分类: Web(2)  版权声明:本文为博主原创文章,未经博主允许不得转载. 最近在做一个SpringMVC+spring+MyBatis的项目,突然想起以前自己要搭建一个Struts2+Sprin ...