2019icpc上海区域赛 F A Simple Problem On A Tree

F A Simple Problem On A Tree

题意:一棵树,有权值,4种操作,
1:u,v路径上的点权值全部为w
2:u,v路径上的点权值全部加w
3:u,v路径上的点权值全部乘w
4:u,v路径上的点 w 3 w^3 w3

思路:
首先肯定是要把树上的操作转换为连续区间,所以(树)刨一下,然后接下来就是线段树操作了。
线段树操作:
w 3 w^3 w3的和,所以我们维护一个sum3,表示 w 3 w^3 w3。建树pushup(初始情况)
然后对于更新操作,假设a,b,c,加上x, ( a + x ) 3 + ( b + x ) 3 + ( c + x ) 3 = a 3 + b 3 + c 3 + 3 ∗ x ∗ ( a 2 + b 2 + c 2 ) + 3 ∗ x 2 ∗ ( a + b + c ) + 3 ∗ x 3 (a+x)^3 + (b + x)^3 + (c + x )^3 = a^3 + b^3 + c^3 +3*x*(a^2 + b^2 + c^2) + 3*x^2*(a+ b + c) + 3*x^3 (a+x)3+(b+x)3+(c+x)3=a3+b3+c3+3x(a2+b2+c2)+3x2(a+b+c)+3x3
然后我们再维护一个sum2( w 2 w^2 w2),sum1( w w w),进行区间加的时候直接加上这个式子就ok。
对于乘法的话,我们只需要把这个区间的sum3* x 3 x^3 x3就是了。
然后对于操作1,我们先对这个区间乘以0,在加上w。
对于lazy标记
有2个(乘法,除法)标记,ad = 0,mu = 1.
然后我们是先乘还是先加?
假设我们先乘在加。
(a + x)y = ay + x*y
所以我们还是先区间乘上y,然后把这个区间的ad乘上y,用更新过的ad去进行加的操作
先加在乘的话就可以依次进行。

Code(1989ms)

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
const int man = 2e5+10;
#define IOS ios::sync_with_stdio(0)
template <typename T>
inline T read(){T sum=0,fl=1;int ch=getchar();
for(;!isdigit(ch);ch=getchar())if(ch=='-')fl=-1;
for(;isdigit(ch);ch=getchar())sum=sum*10+ch-'0';
return sum*fl;}
template <typename T>
inline void write(T x) {static int sta[35];int top=0;
do{sta[top++]= x % 10, x /= 10;}while(x);
while (top) putchar(sta[--top] + 48);}
template<typename T>T gcd(T a, T b) {return b==0?a:gcd(b, a%b);}
template<typename T>T exgcd(T a,T b,T &g,T &x,T &y){if(!b){g = a,x = 1,y = 0;}
else {exgcd(b,a%b,g,y,x);y -= x*(a/b);}}
#ifndef ONLINE_JUDGE
#define debug(fmt, ...) {printf("debug ");printf(fmt,##__VA_ARGS__);puts("");}
#else
#define debug(fmt, ...)
#endif
typedef long long ll;
const ll mod = 1e9+7;

//#define int long long
vector<int>sp[man];
int va[man];
int top[man],rk[man],id[man],siz[man];
int son[man],fa[man],dep[man];
int cnt = 0;
void dfs1(int u,int f){
    siz[u] = 1;
    fa[u] = f;
    dep[u] = dep[f] + 1;
    for(int i = 0;i < sp[u].size();i++){
        int v = sp[u][i];
        if(v==f)continue;
        dfs1(v,u);
        siz[u] += siz[v];
        if(siz[son[u]]<siz[v]){
            son[u] = v;
        }
    }
}

void dfs2(int u,int tp){
    id[++cnt] = u;
    rk[u] = cnt;
    top[u] = tp;
    if(!son[u])return;
    dfs2(son[u],tp);
    for(int i = 0;i < sp[u].size();i++){
        int v = sp[u][i];
        if(v==fa[u]||v==son[u])continue;
        dfs2(v,v);
    }
}

ll sum1[man<<2],sum2[man<<2],sum3[man<<2];
int ad[man<<2],mu[man<<2];
inline void add(ll &x, int y){ 
    (x += y) >= mod ?  x -= mod : x; 
}
inline void mul(ll &x, int y){
    x = 1ll * x * y % mod; 
}
void pushup(int rt){
    sum1[rt] = (sum1[rt<<1] + sum1[rt<<1|1])%mod;
    sum2[rt] = (sum2[rt<<1] + sum2[rt<<1|1])%mod;
    sum3[rt] = (sum3[rt<<1] + sum3[rt<<1|1])%mod;
}

inline void pushdown1(int rt,int x,int y,int num){
    if(y!=1){//*
        int w1 = y;
        int w2 = 1ll*y*y%mod;
        int w3 = 1ll*w2*y%mod;
        mul(sum1[rt],w1);
        mul(sum2[rt],w2);
        mul(sum3[rt],w3);

        mu[rt] = 1ll*mu[rt]*y%mod;
        ad[rt] = 1ll*ad[rt]*y%mod;
    }
    if(x!=0){//+
        int w1 = x;
        int w2 = 1ll*x*x%mod;
        int w3 = 1ll*w2*x%mod;
        add(sum3[rt],3ll*w1%mod*sum2[rt]%mod);
        add(sum3[rt],3ll*w2%mod*sum1[rt]%mod);
        add(sum3[rt],1ll*num*w3%mod);

        add(sum2[rt],1ll*num*w2 % mod);
        add(sum2[rt],2LL*w1*sum1[rt] % mod);

        add(sum1[rt],1ll*num*w1%mod);

        ad[rt] = (1ll*ad[rt] + w1)%mod;
    }
}

inline void pushdown(int rt,int ls,int rs){
    int x = ad[rt];
    int y = mu[rt];
    pushdown1(rt<<1,x,y,ls);
    pushdown1(rt<<1|1,x,y,rs);
    ad[rt] = 0;
    mu[rt] = 1;
}

inline void build(int l,int r,int rt){
    sum1[rt] = sum2[rt] = sum3[rt] = 0;
    ad[rt] = 0;
    mu[rt] = 1;
    if(l==r){
        //cout << "l:" << l << " " << id[l] << endl;
        sum1[rt] = va[id[l]];
        sum2[rt] = 1ll * va[id[l]] * va[id[l]] % mod;
        sum3[rt] = 1ll * sum2[rt] * va[id[l]] % mod;
        return;
    }
    int m = l + r >> 1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
    pushup(rt);
}

inline void update(int l,int r,int L,int R,int rt,int op,int v){
    if(L<=l&&R>=r){
        //更新
        if(op==1){
            pushdown1(rt,v,0,r - l + 1);
        }else if(2==op){
            pushdown1(rt,v,1,r - l + 1);
        }else{
            pushdown1(rt,0,v,r - l + 1);
        }
        return;
    }
    int m = l + r >> 1;
    pushdown(rt,m-l+1,r-m);
    if(L<=m)update(l,m,L,R,rt<<1,op,v);
    if(R>m)update(m+1,r,L,R,rt<<1|1,op,v);
    pushup(rt);
}

inline int query(int l,int r,int L,int R,int rt){
    if(L<=l&&R>=r){
        //cout << l << " l:r " << r << " " << L << " L:R " << R <<endl;
        return sum3[rt];
    }
    int m = l + r >> 1;
    pushdown(rt,m-l+1,r-m);
    int ans = 0;
    if(L<=m)ans = (ans + query(l,m,L,R,rt<<1))%mod;
    if(R>m)ans = (ans + query(m+1,r,L,R,rt<<1|1))%mod;
    pushup(rt);
    return ans;
}

void update(int u,int v,int op,int V){
    while(top[u]!=top[v]){
        if(dep[top[u]]<dep[top[v]]){
            swap(u,v);
        }
        update(1,cnt,rk[top[u]],rk[u],1,op,V);
        u = fa[top[u]];
    }
    if(dep[u]<dep[v])swap(u,v);
    update(1,cnt,rk[v],rk[u],1,op,V);
}

ll query(int u,int v){
    ll ans = 0;
    while(top[u]!=top[v]){
        if(dep[top[u]]<dep[top[v]]){
            swap(u,v);
        }
        int tp = query(1,cnt,rk[top[u]],rk[u],1);
        add(ans,tp);
        //cout <<"u:" << u << " " << v << " " << top[u] << " " << u  << " " << ans << "tp:" << tp << endl;
        u = fa[top[u]];
    }
    if(dep[u]<dep[v])swap(u,v);
    add(ans,query(1,cnt,rk[v],rk[u],1));
    //cout <<"u:" << u << " " << v << " " << top[u] << " " << u <<" " << ans << endl;
    return ans;
}

signed main() {
    #ifndef ONLINE_JUDGE
        freopen("in.txt", "r", stdin);
        //freopen("out.txt","w",stdout);
    #endif
    int t,T = 0;
    scanf("%d",&t);
    //cout << t << endl;
    while(t--){
        int n;
        scanf("%d",&n);
        for(int i = 1;i <= n;i++)sp[i].clear(),son[i] = 0;
        for(int i = 1;i < n;i++){
            int u,v;
            scanf("%d%d",&u,&v);
            sp[u].push_back(v);
            sp[v].push_back(u);
        }
        for(int i = 1;i <= n;i++)scanf("%d",&va[i]);
        dep[0] = 0;cnt = 0;
        dfs1(1,0);
        dfs2(1,1);
        build(1,cnt,1);
        //cout << "!!!!" << endl;
        printf("Case #%d: \n",++T);
        int q;scanf("%d",&q);
        while(q--){
            int op,u,v,w;
            scanf("%d",&op);
            scanf("%d%d",&u,&v);
            if(4!=op){
                scanf("%d",&w);
                update(u,v,op,w);
            }else{
                printf("%d\n",query(u,v));
            }
        }
    }   
    return 0;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值