A Simple Problem On A Tree

Posted 1024-xzx

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了A Simple Problem On A Tree相关的知识,希望对你有一定的参考价值。

题意:

给出一棵树,树上每个点有一个权值,有如下操作:
1.输入 (u,v,w),把点 (u,v) 之间路径上的点的权值全部赋值为 (w)
2.输入 (u,v,w),把点 (u,v) 之间路径上的点的权值全部加上 (w)
3.输入 (u,v,w),把点 (u,v) 之间路径上的点的权值全部乘以 (w)
4.输入 (u,v),求出 (sum_{x}{W_x^3}),即路径上所有点的立方和;
数据范围:$ 0 leq w leq 1,000,000,000,1≤N≤100,000$
传送门

分析:

树链剖分,线段树维护区间(平方和,立方和)修改区间(加,赋值,乘)。

维护立方和,要同时维护平方和与一次方和。
同时处理加法和乘法处理时,先处理乘法,同时注意乘法对加法的影响,如:((a+b)*c=a*c+b*c)
另外,赋值的处理先于乘法和加法。
维护平方和和立方和,根据:
((w+d)^3=w^3+(d^3+3*w^2*d+3*w*d^2),(w*d)^3=w^3*d^3)
((w+d)^2=w^2+(d^2+2*w*d),(w*d)^2=w^2*d^2)
另外立方和的处理先于平方和,平方和的处理先于一次方的处理。
注意取模。

代码:

#include <bits/stdc++.h>
#define pb push_back
using namespace std;
typedef long long ll;
const ll mod=1e9+7;
const int N=1e5+5;
struct node
{
    ll val,sqa,cub;
}tree[N<<2];
struct tag
{
    ll add,mul,asg;
}lazy[N<<2];
vector<int>pic[N];
ll w[N];
int dfn[N],rnk[N],sz[N],son[N],depth[N],fa[N],top[N];
int n;
/**重链剖分**/
void dfs1(int v,int p,int d)
{
    sz[v]=1;
    son[v]=0;
    depth[v]=d;
    fa[v]=p;
    for(int i=0;i<pic[v].size();i++)
    {
        int u=pic[v][i];
        if(u==p) continue;
        dfs1(u,v,d+1);
        sz[v]+=sz[u];
        if(sz[u]>sz[son[v]])
            son[v]=u;
    }
}
void dfs2(int v,int p,int tp,int &cnt)
{
    dfn[v]=++cnt;
    rnk[cnt]=v;
    top[v]=tp;
    if(!son[v]) return;
    dfs2(son[v],v,tp,cnt);
    for(int i=0;i<pic[v].size();i++)
    {
        int u=pic[v][i];
        if(u==p||u==son[v]) continue;
        dfs2(u,v,u,cnt);
    }
}
/**线段树**/
void pushup(int rt)
{
    tree[rt].val=(tree[rt<<1].val+tree[rt<<1|1].val)%mod;
    tree[rt].sqa=(tree[rt<<1].sqa+tree[rt<<1|1].sqa)%mod;
    tree[rt].cub=(tree[rt<<1].cub+tree[rt<<1|1].cub)%mod;
}
void pushdown(int rt,int ln,int rn)
{//先判断是否是赋值:
    //先乘后加:
    if(lazy[rt].asg)//赋值要优先于加和乘处理
    {
        ll t=lazy[rt].asg%mod;
        tree[rt<<1].cub=t*t%mod*t%mod*ln%mod;
        tree[rt<<1|1].cub=t*t%mod*t%mod*rn%mod;
        tree[rt<<1].sqa=t*t%mod*ln%mod;
        tree[rt<<1|1].sqa=t*t%mod*rn%mod;
        tree[rt<<1].val=t%mod*ln%mod;
        tree[rt<<1|1].val=t%mod*rn%mod;
        lazy[rt<<1]=tag{0,1,t};
        lazy[rt<<1|1]=tag{0,1,t};
        lazy[rt].asg=0;//赋值处理后不能把lazy[rt]的加和乘的标记给取消
    }
    if(lazy[rt].mul>1)
    {
        ll t=lazy[rt].mul*lazy[rt].mul%mod*lazy[rt].mul%mod;
        tree[rt<<1].cub=tree[rt<<1].cub*t%mod;
        tree[rt<<1|1].cub=tree[rt<<1|1].cub*t%mod;

        t=lazy[rt].mul*lazy[rt].mul%mod;
        tree[rt<<1].sqa=tree[rt<<1].sqa*t%mod;
        tree[rt<<1|1].sqa=tree[rt<<1|1].sqa*t%mod;

        tree[rt<<1].val=tree[rt<<1].val*lazy[rt].mul%mod;
        tree[rt<<1|1].val=tree[rt<<1|1].val*lazy[rt].mul%mod;

        lazy[rt<<1].add=lazy[rt<<1].add*lazy[rt].mul%mod;
        lazy[rt<<1].mul=lazy[rt<<1].mul*lazy[rt].mul%mod;
        lazy[rt<<1|1].add=lazy[rt<<1|1].add*lazy[rt].mul%mod;
        lazy[rt<<1|1].mul=lazy[rt<<1|1].mul*lazy[rt].mul%mod;
        lazy[rt].mul=1;
    }
    if(lazy[rt].add>0)
    {//(w+d)^3=w^3+(d^3+3*w^2*d+3*w*d^2)
        ll t=lazy[rt].add*lazy[rt].add%mod*lazy[rt].add%mod;
        ll a=3LL*tree[rt<<1].sqa%mod*lazy[rt].add%mod;
        ll b=3LL*tree[rt<<1].val%mod*lazy[rt].add%mod*lazy[rt].add%mod;
        tree[rt<<1].cub=(tree[rt<<1].cub+t*ln%mod+a+b)%mod;

        a=3LL*tree[rt<<1|1].sqa%mod*lazy[rt].add%mod;
        b=3LL*tree[rt<<1|1].val%mod*lazy[rt].add%mod*lazy[rt].add%mod;
        tree[rt<<1|1].cub=(tree[rt<<1|1].cub+t*rn%mod+a+b)%mod;
        //(w+d)^2=w^2+(d^2+2*w*d)
        t=lazy[rt].add*lazy[rt].add%mod;
        a=2LL*tree[rt<<1].val%mod*lazy[rt].add%mod;
        tree[rt<<1].sqa=(tree[rt<<1].sqa+t*ln%mod+a)%mod;

        a=2LL*tree[rt<<1|1].val%mod*lazy[rt].add%mod;
        tree[rt<<1|1].sqa=(tree[rt<<1|1].sqa+t*rn%mod+a)%mod;
        //
        tree[rt<<1].val=(tree[rt<<1].val+lazy[rt].add*ln%mod)%mod;
        tree[rt<<1|1].val=(tree[rt<<1|1].val+lazy[rt].add*rn%mod)%mod;

        lazy[rt<<1].add=(lazy[rt<<1].add+lazy[rt].add)%mod;
        lazy[rt<<1|1].add=(lazy[rt<<1|1].add+lazy[rt].add)%mod;
        lazy[rt].add=0;
    }
}
void build(int l,int r,int rt)
{
    lazy[rt]=tag{0,1,0};
    if(l==r)
    {
        int t=rnk[l];
        tree[rt].val=w[t];
        tree[rt].sqa=w[t]*w[t]%mod;
        tree[rt].cub=w[t]*w[t]%mod*w[t]%mod;
        return;
    }
    int mid=(l+r)>>1;
    build(l,mid,rt<<1);
    build(mid+1,r,rt<<1|1);
    pushup(rt);
}
void update(int l,int r,int L,int R,int rt,ll num,int f)
{
    if(L<=l&&r<=R)
    {
        if(f==1)//直接赋值
        {
            tree[rt].val=num%mod*(r-l+1)%mod;
            tree[rt].sqa=num%mod*num%mod*(r-l+1)%mod;
            tree[rt].cub=num%mod*num%mod*num%mod*(r-l+1)%mod;
            lazy[rt]=tag{0,1,num};
        }
        else if(f==2)//加
        {//立方先加
            //立方:(w+d)^3=w^3+(d^3+3*w^2*d+3*w*d^2)
            ll t=(num%mod*num%mod*num%mod*(r-l+1)%mod+3LL*tree[rt].sqa%mod*num%mod+3LL*num*num%mod*tree[rt].val%mod)%mod;
            tree[rt].cub=(tree[rt].cub+t)%mod;
            //平方:w^2+d^2+2*w*d=(w+d)^2
            t=(num%mod*num%mod*(r-l+1)%mod+2LL*num%mod*tree[rt].val%mod)%mod;
            tree[rt].sqa=(tree[rt].sqa+t)%mod;
            //一次方:
            tree[rt].val=(tree[rt].val+num*(r-l+1)%mod)%mod;
            
            lazy[rt].add=(lazy[rt].add+num)%mod;
        }
        else//乘
        {//注意对加法的影响
            //立方:(w*d)^3=(w^3)*(d^3)
            tree[rt].cub=(tree[rt].cub*num%mod*num%mod*num)%mod;
            //平方:(w*d)^2=w^2*d^2
            tree[rt].sqa=(tree[rt].sqa*num%mod*num)%mod;
            //一次方:
            tree[rt].val=(tree[rt].val*num)%mod;
            lazy[rt].add=lazy[rt].add*num%mod;
            lazy[rt].mul=lazy[rt].mul*num%mod;
        }
        return;
    }
    int mid=(l+r)>>1;
    pushdown(rt,mid-l+1,r-mid);
    if(L<=mid)
        update(l,mid,L,R,rt<<1,num,f);
    if(R>mid)
        update(mid+1,r,L,R,rt<<1|1,num,f);
    pushup(rt);
}
ll query(int l,int r,int L,int R,int rt)
{
    if(L<=l&&r<=R)
        return tree[rt].cub%mod;
    int mid=(l+r)>>1;
    pushdown(rt,mid-l+1,r-mid);
    ll ans=0;
    if(L<=mid)
        ans=(ans+query(l,mid,L,R,rt<<1))%mod;
    if(R>mid)
        ans=(ans+query(mid+1,r,L,R,rt<<1|1))%mod;
    return ans;
}
void change(int u,int v,ll w,int f)
{
    while(top[u]!=top[v])
    {
        if(depth[top[v]]<depth[top[u]]) swap(u,v);
        update(1,n,dfn[top[v]],dfn[v],1,w,f);
        v=fa[top[v]];
    }
    if(depth[v]<depth[u]) swap(u,v);
    update(1,n,dfn[u],dfn[v],1,w,f);
}
ll ask(int u,int v)
{
    ll res=0;
    while(top[u]!=top[v])
    {
        if(depth[top[v]]<depth[top[u]]) swap(u,v);
        res=(res+query(1,n,dfn[top[v]],dfn[v],1))%mod;
        v=fa[top[v]];
    }
    if(depth[v]<depth[u]) swap(u,v);
    res=(res+query(1,n,dfn[u],dfn[v],1))%mod;
    return res;
}
/**初始化**/
void init()
{
    for(int i=1;i<=n;i++)
        pic[i].clear();
}
int main()
{
    int t,x,y,cas=0,q;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        init();
        for(int i=1;i<n;i++)
        {
            scanf("%d%d",&x,&y);
            pic[x].pb(y);
            pic[y].pb(x);
        }
        for(int i=1;i<=n;i++)
            scanf("%lld",&w[i]);
        int cnt=0;
        dfs1(1,0,0);
        dfs2(1,0,1,cnt);
        build(1,n,1);
        scanf("%d",&q);
        printf("Case #%d:
",++cas);
        while(q--)
        {
            int op,u,v;
            ll we;
            scanf("%d",&op);
            scanf("%d%d",&u,&v);
            if(op==4)
                printf("%lld
",ask(u,v));
            else
            {
                scanf("%lld",&we);
                change(u,v,we,op);
            }
        }
    }
    return 0;
}













以上是关于A Simple Problem On A Tree的主要内容,如果未能解决你的问题,请参考以下文章

2019 ICPC上海 F.A Simple Problem On A Tree(树链剖分)

bzoj 3489: A simple rmq problem

HDU 1757 A Simple Math Problem

poj 3466 A Simple Problem with Integers

A Simple Problem

A Simple Problem