树上路径 牛客网

https://www.nowcoder.com/acm/contest/180/E

树剖 前两种是基本更新操作 第三种查询操作需要稍微转换一下

题目要求的式子 a1*(a2+a3+...+an)+a2*(a3+a3+...+an)+...+an-1*an 显然这是个上三角

原式=(a1*(a1+a2+a3+...+an)+a2*(a1+a2+a3+...+an)+...+an*(a1+a2+a3+...+an)-(a1*a1+a2*a2+...+an*an))/2

       =((a1+a2+a3+...+an)*(a1+a2+a3+...+an)-(a1*a1+a2*a2+...+an*an))/2

维护一下一次幂与二次幂之和就好 和HDU4578一样处理 详见https://blog.csdn.net/sunyutian1998/article/details/80397920

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const ll M=1000000007;
const ll ni=500000004;

struct node
{
    int v;
    int next;
};

node edge[200010];
ll sum1[400010],sum2[400010],laz[400010];
ll pre[100010];
int first[100010],fa[100010],deep[100010],sum[100010],son[100010],top[100010],mp1[100010],mp2[100010];
int n,q,num;

void addedge(int u,int v)
{
    edge[num].v=v;
    edge[num].next=first[u];
    first[u]=num++;
    return;
}

void dfsI(int cur)
{
    int i,v;
    sum[cur]=1,son[cur]=-1;
    for(i=first[cur];i!=-1;i=edge[i].next)
    {
        v=edge[i].v;
        if(v!=fa[cur])
        {
            fa[v]=cur,deep[v]=deep[cur]+1;
            dfsI(v);
            sum[cur]+=sum[v];
            if(son[cur]==-1||sum[son[cur]]<sum[v])
            {
                son[cur]=v;
            }
        }
    }
    return;
}

void dfsII(int cur,int tp)
{
    int i,v;
    num++;
    top[cur]=tp,mp1[cur]=num,mp2[num]=cur;
    if(son[cur]==-1) return;
    dfsII(son[cur],tp);
    for(i=first[cur];i!=-1;i=edge[i].next)
    {
        v=edge[i].v;
        if(v!=fa[cur]&&v!=son[cur])
        {
            dfsII(v,v);
        }
    }
    return;
}

void change(int cur,ll len,ll val)
{
    sum2[cur]=(sum2[cur]+(2ll*val*sum1[cur])%M+((len*val)%M*val)%M)%M;
    sum1[cur]=(sum1[cur]+(len*val)%M)%M;
    laz[cur]=(laz[cur]+val)%M;
}

void pushup(int cur)
{
    sum1[cur]=(sum1[2*cur]+sum1[2*cur+1])%M;
    sum2[cur]=(sum2[2*cur]+sum2[2*cur+1])%M;
}

void pushdown(int cur,int l,int r)
{
    ll len;
    int m;
    if(laz[cur]!=0)
    {
        m=(l+r)/2;
        change(2*cur,m-l+1,laz[cur]);
        change(2*cur+1,r-m,laz[cur]);
        laz[cur]=0;
    }
}

void build(int l,int r,int cur)
{
    int m;
    if(l==r)
    {
        sum1[cur]=pre[mp2[l]];
        sum2[cur]=(pre[mp2[l]]*pre[mp2[l]])%M;
        return;
    }
    m=(l+r)/2;
    build(l,m,2*cur);
    build(m+1,r,2*cur+1);
    pushup(cur);
}

void updateII(int pl,int pr,ll val,int l,int r,int cur)
{
    int m;
    if(pl<=l&&r<=pr)
    {
        change(cur,r-l+1,val);
        return;
    }
    pushdown(cur,l,r);
    m=(l+r)/2;
    if(pl<=m) updateII(pl,pr,val,l,m,2*cur);
    if(pr>m) updateII(pl,pr,val,m+1,r,2*cur+1);
    pushup(cur);
}

void updateI(int u,int v,ll val)
{
    while(top[u]!=top[v])
    {
        if(deep[top[u]]<deep[top[v]]) swap(u,v);
        updateII(mp1[top[u]],mp1[u],val,1,n,1);
        u=fa[top[u]];
    }
    if(deep[u]<deep[v]) swap(u,v);
    updateII(mp1[v],mp1[u],val,1,n,1);
    return;
}

void queryII(int pl,int pr,ll &res1,ll &res2,int l,int r,int cur)
{
    int m;
    if(pl<=l&&r<=pr)
    {
        res1=(res1+sum1[cur])%M;
        res2=(res2+sum2[cur])%M;
        return;
    }
    pushdown(cur,l,r);
    m=(l+r)/2;
    if(pl<=m) queryII(pl,pr,res1,res2,l,m,2*cur);
    if(pr>m) queryII(pl,pr,res1,res2,m+1,r,2*cur+1);
}

void queryI(int u,int v,ll &res1,ll &res2)
{
    while(top[u]!=top[v])
    {
        if(deep[top[u]]<deep[top[v]]) swap(u,v);
        queryII(mp1[top[u]],mp1[u],res1,res2,1,n,1);
        u=fa[top[u]];
    }
    if(deep[u]<deep[v]) swap(u,v);
    queryII(mp1[v],mp1[u],res1,res2,1,n,1);
    return;
}

int main()
{
    ll val,res1,res2;
    int i,op,u,v;
    scanf("%d%d",&n,&q);
    for(i=1;i<=n;i++) scanf("%lld",&pre[i]);
    memset(first,-1,sizeof(first));
    num=0;
    for(i=1;i<=n-1;i++)
    {
        scanf("%d%d",&u,&v);
        addedge(u,v);
        addedge(v,u);
    }
    fa[1]=0,deep[1]=1;
    dfsI(1);
    num=0;
    dfsII(1,1);
    build(1,n,1);
    while(q--)
    {
        scanf("%d",&op);
        if(op==1)
        {
            scanf("%d%lld",&u,&val);
            updateII(mp1[u],mp1[u]+sum[u]-1,val,1,n,1);
        }
        else if(op==2)
        {
            scanf("%d%d%lld",&u,&v,&val);
            updateI(u,v,val);
        }
        else
        {
            scanf("%d%d",&u,&v);
            res1=0,res2=0;
            queryI(u,v,res1,res2);
            val=(((res1*res1)%M-res2+M)*ni)%M;
            printf("%lld\n",val);
        }
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值