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

树链剖分若不会的话可自行学习一下.

前两种操作是线性变换,模\(2^{64}\)可将线段树全部用unsigned long long 保存,另其自然溢出.

而取反操作比较不能直接处理,因为其模\(2^{64}\)的特殊性,可将其转化为线性变换.

显然

\[-x\equiv (2^{64}-1)*x (mod\ 2^{64})\]

因为\[!x = (2^{64}-1) -x \]

所以

\[ !x = (2^{64}-1) + (2^{64}-1)x\]

#include<bits/stdc++.h>
#define lson rt<<1
#define rson rt<<1|1
#define Lson l,m,lson
#define Rson m+1,r,rson
typedef unsigned long long LL;
LL TTT = 0xffffffffffffffff;
using namespace std;
const int maxn =1e5+5;
struct Edge{
    int to,next;
}E[2*maxn];
int n,head[maxn],tot;
int cnt,idx,size[maxn],fa[maxn],son[maxn],dep[maxn],top[maxn],id[maxn],rnk[maxn];

void init()
{
    cnt=idx=tot=0;
    memset(head,-1,sizeof(head));
    dep[1]=0,fa[1]=1,size[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)
{
    size[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);
            size[u]+=size[v];
            if(size[son[u]]<size[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);
    }
}

struct Node{
    LL sum,add,b;
    bool nt;
}tree[maxn<<2];
void pushup(int rt){
    tree[rt].sum = tree[lson].sum + tree[rson].sum;
}

void pushdown(int l,int r,int rt)
{
    int m = (l+r)>>1;
    if(tree[rt].add!=1){
        tree[lson].sum *= tree[rt].add;
        tree[rson].sum *= tree[rt].add;
        tree[lson].b *= tree[rt].add;
        tree[rson].b *= tree[rt].add;
        tree[lson].add *= tree[rt].add;
        tree[rson].add *= tree[rt].add;
        tree[rt].add = 1;
    }
    if(tree[rt].b){
        tree[lson].sum += (m-l+1)* tree[rt].b;
        tree[rson].sum += (r-m) *tree[rt].b;
        tree[lson].b += tree[rt].b;
        tree[rson].b += tree[rt].b;
        tree[rt].b= 0;
    }
}

void build(int l,int r,int rt)
{
    tree[rt].add =1;
    tree[rt].b =0;
    tree[rt].nt = 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 k,LL b,int l=1,int r=n,int rt=1)
{
    if(L<=l && R>=r){
        tree[rt].sum *= k;
        tree[rt].add *= k;
        tree[rt].b *= k;
        tree[rt].sum += (r-l+1)*b;
        tree[rt].b +=b;
        tree[rt].nt = 0;
        return;
    }
    pushdown(l,r,rt);
    int m = (l+r)>>1;
    if(L<=m) update(L,R,k,b,Lson);
    if(R>m) update(L,R,k,b,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);
    LL ans=0;
    int m = (l+r)>>1;
    if(L<=m) ans += query(L,R,Lson);
    if(R>m) ans += query(L,R,Rson);
    return ans;
}

void change(int u,int v,int op,LL val)
{
    while(top[u]!=top[v]){
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        if(op==1){
            update(id[top[u]],id[u],(LL)1,val);
        }
        else if(op==2){
            update(id[top[u]],id[u],val,0);
        }
        else{
            update(id[top[u]],id[u],TTT,TTT);
        }
        u = fa[top[u]];
    }
    if(dep[u]>dep[v]) swap(u,v);
    if(op==1){
        update(id[u],id[v],(LL)1,val);
    }
    else if(op==2){
        update(id[u],id[v],val,0);
    }
    else{
        update(id[u],id[v],TTT,TTT);
    }
    return ;
}

LL Qsum(int u,int v)
{
    LL ans=0;
    while(top[u]!=top[v]){
        if(dep[top[u]]<dep[top[v]]) swap(u,v);
        ans += query(id[top[u]],id[u]);
        u = fa[top[u]];
    }
    if(dep[u]>dep[v]) swap(u,v);
    ans += query(id[u],id[v]);
    return ans;
}

int main()
{
    #ifndef ONLINE_JUDGE
        freopen("in.txt","r",stdin);
        freopen("out.txt","w",stdout);
    #endif
    int u,v,Q;
    while(scanf("%d",&n)==1){
        init();
        for(int i=2;i<=n;++i){
            scanf("%d",&u);
            AddEdge(u,i);
            AddEdge(i,u);
        }
        dfs1(1);
        dfs2(1,1);
        build(1,n,1);
        scanf("%d",&Q);
        int op;
        LL tmp;
        while(Q--){
            scanf("%d",&op);
            if(op==1){
                scanf("%d %d %lld",&u, &v, &tmp);
                change(u,v,2,tmp);
            }
            else if(op==2){
                scanf("%d %d %llu",&u, &v,&tmp);
                change(u,v,1,tmp);
            }
            else if(op==3){
                scanf("%d %d",&u, &v);
                change(u,v,3,0);
            }
            else{
                scanf("%d %d",&u, &v);
                printf("%llu\n",Qsum(u,v));
            }
        }
    }
    return 0;
}

原文地址:https://www.cnblogs.com/xiuwenli/p/9651737.html

时间: 2024-11-05 04:54:08

ACM-ICPC 2018 焦作赛区网络预赛 E. Jiu Yuan Wants to Eat (树链剖分-线性变换线段树)的相关文章

ACM-ICPC 2018 焦作赛区网络预赛 E. Jiu Yuan Wants to Eat

分析 除了树剖没想到其他解法. 用线段树维护区间和,同时针对修改区间修改操作建立两个lazy标记,一个是\(lazy_{mul}\),另一个是\(lazy_{add}\),代表区间里的数都需要先乘以\(lazy_{mul}\),再加上\(lazy_{add}\).如果一个区间需要被重复标记,那么我们可以先把新的lazy标记施加在原有的lazy上. 如果是区间乘以val,那么就可以$ lazy_{mul}=lazy_{mul} \times val,lazy_{add}=lazy_{add}\ti

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取模的,我们可以 尝试把这个式子转换成只有加法和乘法的,这样就可以将其和前面

ACM-ICPC 2018 焦作赛区网络预赛 H题 String and Times(SAM)

Now you have a string consists of uppercase letters, two integers AA and BB. We call a substring wonderful substring when the times it appears in that string is between AA and BB (A \le times \le BA≤times≤B). Can you calculate the number of wonderful

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

A prince of the Science Continent was imprisoned in a castle because of his contempt for mathematics when he was young, and was entangled in some mathematical curses. He studied hard until he reached adulthood and decided to use his knowledge to esca

ACM-ICPC 2018 焦作赛区网络预赛 L 题 Poor God Water

God Water likes to eat meat, fish and chocolate very much, but unfortunately, the doctor tells him that some sequence of eating will make them poisonous. Every hour, God Water will eat one kind of food among meat, fish and chocolate. If there are 33 

ACM-ICPC 2018 焦作赛区网络预赛 K题 Transport Ship

There are NN different kinds of transport ships on the port. The i^{th}ith kind of ship can carry the weight of V[i]V[i] and the number of the i^{th}ith kind of ship is 2^{C[i]} - 12C[i]?1. How many different schemes there are if you want to use thes

ACM-ICPC 2018 焦作赛区网络预赛 L:Poor God Water(矩阵快速幂)

God Water likes to eat meat, fish and chocolate very much, but unfortunately, the doctor tells him that some sequence of eating will make them poisonous. Every hour, God Water will eat one kind of food among meat, fish and chocolate. If there are 3 c

ACM-ICPC 2018 焦作赛区网络预赛 B. Mathematical Curse &lt;&lt; DP

题意 有n个数和m个运算符,按顺序选m个数进行运算,初值为k,问最后能得到的最大值是多少. 思路 dp[i][j]表示选到了第i个数时用了j个运算符,观察发现,一个数只能由他前一个状态的最大值或最小值转移过来(因为乘上一个负数会使最小的数变最大),所以我们同时维护最大最小. 然后转移就行了,需要注意$i-1>j$时是不合法的状态. 转移方程:$dp[i][j]=max(dp[i-1][j-1]\circ now[i],dp[i-1][j])(i<j)$ 代码 1 #include<bit

ACM-ICPC 2018 焦作赛区网络预赛 F. Modular Production Line (区间K覆盖-最小费用流)

很明显的区间K覆盖模型,用费用流求解.只是这题N可达1e5,需要将点离散化. 建模方式步骤: 1.对权值为w的区间[u,v],加边id(u)->id(v+1),容量为1,费用为-w; 2.对所有相邻的点加边id(i)->id(i+1),容量为正无穷,费用为0; 3.建立源点汇点,由源点s向最左侧的点加边,容量为K,费用为0,由最右侧的点向汇点加边,容量为K,费用为0 4.跑出最大流后,最小费用取绝对值就是能获得的最大权 #include<bits/stdc++.h> using n