2018.9 ECNU ICPC/CCPC Trial Round #2 Query On Tree (树链剖分+线段树维护)
一棵树,支持两种操作:给一条路径上的节点加上一个等差数列;求两点路径上节点和.
很明显,熟练剖分.用线段树维护链上的区间和,每个节点中记录等差数列的首项,公差和区间和.因为两个等差数列叠加之后还是等差数列,所以将首项与公差视作懒惰标记.
因为在寻找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 (树链剖分+线段树维护)的更多相关文章
- 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 ... 
- 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 ... 
- 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 ... 
- 2018牛客网暑假ACM多校训练赛(第七场)I Tree Subset Diameter 动态规划 长链剖分 线段树
		原文链接https://www.cnblogs.com/zhouzhendong/p/NowCoder-2018-Summer-Round7-I.html 题目传送门 - https://www.n ... 
- 2019年ICPC南昌网络赛 J. Distance on the tree 树链剖分+主席树
		边权转点权,每次遍历到下一个点,把走个这条边的权值加入主席树中即可. #include<iostream> #include<algorithm> #include<st ... 
- 从lca到树链剖分 bestcoder round#45 1003
		bestcoder round#45 1003 题,给定两个点,要我们求这两个点的树上路径所经过的点的权值是否出现过奇数次.如果是一般人,那么就是用lca求树上路径,然后判断是否出现过奇数次(用异或) ... 
- [集训队作业2018]蜀道难——TopTree+贪心+树链剖分+链分治+树形DP
		题目链接: [集训队作业2018]蜀道难 题目大意:给出一棵$n$个节点的树,要求给每个点赋一个$1\sim n$之内的权值使所有点的权值是$1\sim n$的一个排列,定义一条边的权值为两端点权值差 ... 
- Codeforces Round #620 F2. Animal Observation (hard version) (dp + 线段树)
		Codeforces Round #620 F2. Animal Observation (hard version) (dp + 线段树) 题目链接 题意 给定一个nm的矩阵,每行取2k的矩阵,求总 ... 
- [Codeforces Round #254 div1] C.DZY Loves Colors 【线段树】
		题目链接:CF Round #254 div1 C 题目分析 这道题目是要实现区间赋值的操作,同时还要根据区间中原先的值修改区间上的属性权值. 如果直接使用普通的线段树区间赋值的方法,当一个节点表示的 ... 
随机推荐
- cookie绕过验证码并关联对话发送一个随笔草稿箱
			先手动发送一个草稿,然后用fiddler取到body参数 代码: #coding:utf-8import requests login_url="https://passport.cnblo ... 
- maven + hessian 简单样例
			项目结构例如以下: pom.xml 内容: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi=&quo ... 
- [NOIP2017]列队 离线+SBT
			[NOIP2017]列队 题目描述 Sylvia 是一个热爱学习的女♂孩子. 前段时间,Sylvia 参加了学校的军训.众所周知,军训的时候需要站方阵. Sylvia 所在的方阵中有n×m名学生,方阵 ... 
- 云服务器 ECS Linux 保存用户登录操作命令记录
			转载自 : https://help.aliyun.com/knowledge_detail/41210.html 云服务器 ECS Linux 如果要保存用户登录操作记录,则可以通过在 /etc/p ... 
- LAMP集群项目五 nfs分发文件到服务器
			前边已经配置了免密钥登录,现在脚本直接调用scp即可 ./etc/init.d/functions ] then echo “argv is not correct” exit fi for ip i ... 
- js功能实现的特效--距离新年还有多少天
			代码: <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> & ... 
- Packet for query is too large (1166 > 1024). You can change this value
			转载: MySQL max_allowed_packet 设置过小导致记录写入失败 mysql根据配置文件会限制server接受的数据包大小. 有时候大的插入和更新会受max_allowed_pack ... 
- Zabbix使用SMTP发送邮件报警并且制定报警内容
			接上篇Zabbix监控介绍及安装配置 选择报警项 创建一个报警项 选择到刚刚自定义的80端口 定义报警方法 定义告警级别 一些报警方法 diff 比较是否有修改 last 最低值 nodata 没有数 ... 
- Python全栈day19(函数补充)
			一,深浅拷贝 看拷贝列子day19-1.py s=[1,'zhangsan','lisi'] #s2是s的拷贝 s2=s.copy() #打印s2和s是一样的 print(s2) #修改s2 s2[0 ... 
- Web测试系列之测试工具
			一Web功能测试工具MAXQ MAXQ是开源的Web功能测试工具. MAXQ是开源的Web功能测试工具.他的特点:1)简单易学;2)是一个轻量级的Web功能测试工具;3)可以自动录制WebBrowse ... 
