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

Posted kls123

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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的和

思路:

除去第三个操作就是很简单的树链剖分+线段树多重标记下放,所以我们只要考虑怎么维护第三个操作就好了,

由题目给的取反可知:!x =  (2^64-1) - x;   但是这样维护还是很麻烦,因为这道题是对2^64取模的,我们可以

尝试把这个式子转换成只有加法和乘法的,这样就可以将其和前面两个操作一起维护降低难度。

我们可以想到: (-x)%(2^64) = (2^64-1)*x%(2^64),那么式子就可以转换成:

!x = (2^64-1)*x + (2^64-1) 这样这个式子就只有乘法和加法操作,以及一个常数,然后带入多重标记维护就好了

数组要开 unsigned long long ,题目给的模数也很特殊,2^64, 因为unsigned long long 溢出的时候相当于对2^64

取模,那么我们就不需要取模操作了,直接运算就完事了。

 

实现代码:

#include<bits/stdc++.h>
using namespace std;
#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
#define mid int m = (l + r) >> 1
#define ll  unsigned long long
const ll inf = 18446744073709551615;
const int M = 2e5+10;

vector<int>g[M];
int n,cnt1,cnt,a[M];
int son[M],fa[M],head[M],siz[M],top[M],dep[M],tid[M];
ll sum[M<<2],add[M<<2],mul[M<<2];

void dfs1(int u,int faz,int deep){
     dep[u] = deep;
     fa[u] = faz;
     siz[u] = 1;
     for(int i = 0;i < g[u].size();i++){
        int v = g[u][i];
        //cout<<v<<" ";
        if(v != fa[u]){
            dfs1(v,u,deep+1);
            siz[u] += siz[v];
            if(son[u] == -1||siz[v] > siz[son[u]])
                son[u] = v;
        }
     }
}

void dfs2(int u,int t){
    top[u] = t;
    tid[u] = ++cnt;
    if(son[u] == -1) return;
    dfs2(son[u],t);
    for(int i = 0;i<g[u].size();i++){
        int v = g[u][i];
        if(v != son[u]&&v != fa[u])
            dfs2(v,v);
    }
}

void pushup(int rt){
    sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}

void pushdown(int m,int rt){
    add[rt<<1] = add[rt<<1]*mul[rt] + add[rt];
    add[rt<<1|1] = add[rt<<1|1]*mul[rt]+add[rt];
    mul[rt<<1] = mul[rt<<1]*mul[rt];
    mul[rt<<1|1] = mul[rt<<1|1]*mul[rt];
    sum[rt<<1] = sum[rt<<1]*mul[rt] + add[rt]*(m-(m>>1));
    sum[rt<<1|1] = sum[rt<<1|1]*mul[rt]+add[rt]*(m>>1);
    add[rt] = 0; mul[rt] = 1;
}

void build(int l,int r,int rt){
    add[rt] = 0; mul[rt] = 1;
    if(l == r){
        sum[rt] = 0;
        return;
    }
    mid;
    build(lson); build(rson);
    pushup(rt);
}

void update(int L,int R,ll c,int v,int l,int r,int rt){
    if(L <= l&&R >= r){
        if(v == 1){
            sum[rt] = sum[rt]*c;
            add[rt] = add[rt]*c;
            mul[rt] = mul[rt]*c;
        }
        else if(v == 2){
            sum[rt] = sum[rt] + (ll)c*(r-l+1);
            add[rt] = add[rt]+c;
        }
        else if(v == 3){
            sum[rt] = inf*sum[rt] + (r-l+1)*inf;
            add[rt] = add[rt]*inf+inf;
            mul[rt] *= inf;
        }
        return;
    }
    pushdown(r-l+1,rt);
    int m = (l + r) >> 1;
    if(L <= m) update(L,R,c,v,lson);
    if(R > m) update(L,R,c,v,rson);
    pushup(rt);
}

ll query(int L,int R,int l,int r,int rt){
    if(L <= l&&R >= r){
        return sum[rt];
    }
    mid;
    pushdown(r-l+1,rt);
    ll ret = 0;
    if(L <= m) ret += query(L,R,lson);
    if(R > m)  ret += query(L,R,rson);
    return ret;
}

void cover(int x,int y,ll c,int v){
    int fx = top[x],fy = top[y];
    while(fx!=fy){
        if(dep[fx] < dep[fy]) swap(fx,fy),swap(x,y);
       update(tid[fx],tid[x],c,v,1,n,1);
        x = fa[fx];fx = top[x];
    }
    if(dep[x] < dep[y]) swap(x,y);
    update(tid[y],tid[x],c,v,1,n,1);
}

ll ask(int x,int y){   //求两结点路径上的权值和
    int fx = top[x],fy = top[y];
    ll ans = 0;
    while(fx != fy){
        if(dep[fx] < dep[fy]) swap(fx,fy),swap(x,y);
        ans += query(tid[fx],tid[x],1,n,1);
        x = fa[fx]; fx = top[x];
    }
    ans += (dep[x] > dep[y])?query(tid[y],tid[x],1,n,1):query(tid[x],tid[y],1,n,1);
    return ans;
}

void init(){
    cnt = cnt1 = 0;
    memset(son,-1,sizeof(son));
    dep[1] = 0; fa[1] = 0; siz[0] = 0;
}

int main()
{
    int x,q,op,l,r;
    ll c;
    while(scanf("%d",&n)!=EOF){
        init();
       // cout<<inf<<endl;
        for(int i = 2;i <= n;i ++){
            scanf("%d",&x);
            g[x].push_back(i);
        }
        dfs1(1,0,1);
        dfs2(1,1);
        build(1,n,1);
        scanf("%d",&q);
        while(q--){
            scanf("%d",&op);
            if(op == 1||op == 2){
                scanf("%d%d%llu",&l,&r,&c);
                cover(l,r,c,op);
            }
            else if(op == 3){
                scanf("%d%d",&l,&r);
                cover(l,r,0,op);
            }
            else {
                scanf("%d%d",&l,&r);
                printf("%llu
",ask(l,r));
            }
        }
        for(int i = 1;i <= n;i ++)
            g[i].clear();
    }
    return 0;
}

 

以上是关于ACM-ICPC 2018 焦作赛区网络预赛 E Jiu Yuan Wants to Eat (树链剖分+线段树)的主要内容,如果未能解决你的问题,请参考以下文章

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

c_cpp ACM-ICPC 2018焦作赛区网络预赛

ACM-ICPC 2018 焦作赛区网络预赛 B题 Mathematical Curse

ACM-ICPC 2018 焦作赛区网络预赛 Solution

ACM-ICPC 2018 焦作赛区网络预赛 HL

ACM-ICPC 2018 焦作赛区网络预赛 G题 Give Candies