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 (树链剖分+线段树)
ACM-ICPC 2018 焦作赛区网络预赛 B题 Mathematical Curse