BZOJ4127 Abs (树链剖分 线段树)

    xiaoxiao2021-04-18  52

    题目大意

    给出一棵带权有根树,要求完成以下几种操作:

    1 u v d 表示将路径 (u,v) 加d (0<=d<=1e8) 2 u v 表示询问路径 (u,v) 上点权绝对值的和


    题解

    正常树剖,用线段树维护几个信息:绝对值的和,区间内负数的个数,区间内最大的负数 以及 最大的负数所在的位置,因为有区间加操作,所以还要维护一个加标记。

    这道题棘手的地方就在于,数字的正负性会随着区间加操作而改变,所以说不能直接维护区间和。所以说观察数据范围 (0<=d<=1e8) 可以得知这道题序列中的所有数字,只会从负变正而不会从正变负,所以说序列内数字的正负性最多只改变n次,所以对于每次数字正负性的变化暴力修改就可以了。

    每次处理一个区间加操作之前,先检查区间内进行这个加操作之后是否会有数字的正负性发生改变,也就是说区间内最大的负数改变之后是否大于等于0。如果有的话递归改变那个位置元素的正负性更新线段数,然后再进行加操作。

    其他操作与普通树剖线段数无异,祝大家1A。


    代码

    #include <cstdio> #include <iostream> #include <cmath> #include <algorithm> using namespace std; const int maxx=40000008; char Input[maxx+5],*ipos; #define read() (strtol(ipos,&ipos,10)) const int maxn=int(1e5)+11, inf=1<<30; int n,m,v[maxn]; int tot=0,head[maxn]; struct Edge { int from,to,next; Edge() {} Edge(int x,int y,int nx):from(x),to(y),next(nx) {} }eage[maxn*2]; inline void add(int x,int y) { eage[tot]=Edge(x,y,head[x]), head[x]=tot++; eage[tot]=Edge(y,x,head[y]), head[y]=tot++; return; } int fa[maxn],dep[maxn],siz[maxn],son[maxn]; inline void dfs1(register int u) { siz[u]=1, son[u]=0; for(register int i=head[u];~i;i=eage[i].next) if(eage[i].to!=fa[u]) { fa[eage[i].to]=u; dep[eage[i].to]=dep[u]+1; dfs1(eage[i].to); siz[u]+=siz[eage[i].to]; if(siz[eage[i].to]>siz[son[u]]) son[u]=eage[i].to; } } int top[maxn],seq[maxn],id[maxn],ind; int val[maxn]; inline void dfs2(register int u) { seq[++ind]=u, id[u]=ind; if(son[fa[u]]==u) top[u]=top[fa[u]]; else top[u]=u; if(son[u]) dfs2(son[u]); register int i; for(i=head[u];~i;i=eage[i].next) if(eage[i].to!=fa[u] && eage[i].to!=son[u]) dfs2(eage[i].to); } #define mp make_pair #define ls(k) ((k)<<1) #define rs(k) (ls(k)^1) #define mid ((l+r)>>1) struct Node { long long sum,add; int len,cnt; pair<int,int> pos; inline void get_add(long long); inline void maintain(const Node&,const Node&); }node[maxn*4]; inline void Node:: get_add(long long a) { sum+=(len-2*cnt)*a, add+=a; if(pos.first!=-inf) pos.first+=a; return; } inline void pushdown(int k) { Node &ls=node[ls(k)], &rs=node[rs(k)]; long long &add=node[k].add; if(add) { ls.get_add(add), rs.get_add(add); add=0; } return; } inline void Node:: maintain(const Node &a,const Node &b) { sum=a.sum+b.sum; add=0; len=a.len+b.len; cnt=a.cnt+b.cnt; pos=max(a.pos,b.pos); } inline void build(int k,int l,int r) { node[k].len=r-l+1; if(l==r) { node[k].sum=(int)abs(1.0*val[l]); if(val[l]>=0) node[k].pos=mp(-inf,0); else node[k].cnt=1, node[k].pos=mp(val[l],l); return; } build(ls(k),l,mid); build(rs(k),mid+1,r); node[k].maintain(node[ls(k)],node[rs(k)]); return; } void Build() { build(1,1,n); return; } inline void modify(int k,int l,int r,int ql,int qr,int val) { if(l!=r) pushdown(k); if(ql<=l && r<=qr) { node[k].get_add(val); return; } if(ql<=mid) modify(ls(k),l,mid,ql,qr,val); if(qr> mid) modify(rs(k),mid+1,r,ql,qr,val); node[k].maintain(node[ls(k)],node[rs(k)]); return; } void modify(int l,int r,int a) { modify(1,1,n,l,r,a); return; } inline void flip(int k,int l,int r,int pos) { if(l!=r) pushdown(k); if(l==r) { node[k].sum*=-1; node[k].cnt=0; node[k].pos=mp(-inf,0); return; } if(pos<=mid) flip(ls(k),l,mid,pos); if(pos> mid) flip(rs(k),mid+1,r,pos); node[k].maintain(node[ls(k)],node[rs(k)]); return; } void flip(int pos) { flip(1,1,n,pos); return; } inline pair<int,int> get_pos(int k,int l,int r,int ql,int qr) { if(l!=r) pushdown(k); if(ql<=l && r<=qr) return node[k].pos; pair<int,int> res=mp(-inf,0); if(ql<=mid) res=max(res,get_pos(ls(k),l,mid,ql,qr)); if(qr> mid) res=max(res,get_pos(rs(k),mid+1,r,ql,qr)); return res; } pair<int,int> get_pos(int l,int r) { return get_pos(1,1,n,l,r); } inline long long query(int k,int l,int r,int ql,int qr) { if(l!=r) pushdown(k); if(ql<=l && r<=qr) return node[k].sum; long long res=0; if(ql<=mid) res+=query(ls(k),l,mid,ql,qr); if(qr> mid) res+=query(rs(k),mid+1,r,ql,qr); return res; } long long Query(int l,int r) { return query(1,1,n,l,r); } inline void Modify(int l,int r,int val) { while(true) { pair<int,int> pos=get_pos(l,r); if(pos.first+val>=0) flip(pos.second); else break; } modify(l,r,val); return; } inline void Tree_modify(register int u,register int v,int a) { while(top[u]!=top[v]) { if(dep[top[u]]<dep[top[v]]) std::swap(u,v); Modify(id[top[u]],id[u],a); u=fa[top[u]]; } if(dep[u]>dep[v]) std::swap(u,v); Modify(id[u],id[v],a); return; } inline long long Tree_query(int u,int v) { long long res=0; while(top[u]!=top[v]) { if(dep[top[u]]<dep[top[v]]) std::swap(u,v); res+=Query(id[top[u]],id[u]); u=fa[top[u]]; } if(dep[u]>dep[v]) std::swap(u,v); res+=Query(id[u],id[v]); return res; } int main() { #ifndef ONLINE_JUDGE freopen("input.txt","r",stdin); freopen("output.txt","w",stdout); #endif // ONLINE_JUDGE fread(Input,maxx,1,stdin);ipos=Input; n=read(), m=read(); for(int i=1;i<=n;i++) { head[i]=-1; v[i]=read(); } for(int x,y,i=1;i<n;i++) { x=read(), y=read(); add(x,y); } dep[1]=1, fa[1]=-1; dfs1(1), dfs2(1); for(int i=1;i<=n;i++) val[i]=v[seq[i]]; Build(); int op,u,v,a; for(int i=1;i<=m;i++) { op=read(), u=read(), v=read(); if(op==1) { a=read(); Tree_modify(u,v,a); } else { printf("%lld\n",Tree_query(u,v)); } } return 0; }

    数据生成器:

    #include <cstdio> #include <iostream> #include <algorithm> #include <ctime> using namespace std; const int n=int(1e5), m=n; int maxdep=0; bool vis[n+11]; int f[n+11]; inline int _find(register int k) { if(f[k]==k) return k; return f[k]=_find(f[k]); } int main() { freopen("input0.txt","w",stdout); srand(time(NULL)); printf("%d %d\n",n,m); for(int i=1;i<=n;i++) { int sign=rand()&1; sign=sign?1:-1; printf("%d ",sign*rand()); } printf("\n"); for(int i=1;i<=n;i++) f[i]=i; int tot=0; while(tot<n-1) { int u=(1ll*rand()*rand())%n+1, v=(1ll*rand()*rand())%n+1; int g1=_find(u), g2=_find(v); if(g1!=g2) { f[g1]=g2; printf("%d %d\n",u,v); tot++; } } for(int i=1;i<=m;i++) { int op=rand()%2+1; int u=(1ll*rand()*rand())%n+1, v=(1ll*rand()*rand())%n+1, d=(1ll*rand()*rand())%((int)1e5)+1; if(op==1) printf("%d %d %d %d\n",op,u,v,d); else printf("%d %d %d\n",op,u,v); } return 0; }
    转载请注明原文地址: https://ju.6miu.com/read-674673.html

    最新回复(0)