BZOJ[4127] Abs

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=4127

不算难的样子,才见过此类模型。

首先可以发现每次修改只增不减,那么这$O(n)$的负数最多只会有$n$次由负变正。

所以对于每一次由负变正我们暴力在线段树上维护,每一次由负变正在线段树上会经过$O(logn)$层。

效率$O(logn)$。

其他的维护一下区间负数的个数什么的就可以搞了。

一遍AC爽

  1 #include <cstdio>
  2 #include <cstring>
  3 #include <algorithm>
  4
  5 #define l(x) ch[x][0]
  6 #define r(x) ch[x][1]
  7 #define N 100010
  8 #define LL long long
  9 #define INF 0x3f3f3f3f3f3f3f3fLL
 10
 11 using namespace std;
 12
 13 struct edge{
 14     int x,to;
 15 }E[N<<1];
 16
 17 int n,m,totE;
 18 int g[N];
 19
 20 inline int Abs(int x){
 21     if(x<0) return -x;
 22     return x;
 23 }
 24
 25 inline void ade(int x,int y){
 26     E[++totE]=(edge){y,g[x]}; g[x]=totE;
 27 }
 28
 29 namespace Segtree{
 30     int tot;
 31     int ch[N<<1][2];
 32     int totf[N<<1],siz[N<<1];
 33     LL maxf[N<<1],sumv[N<<1],addv[N<<1];
 34
 35     void push(int x){
 36         if(!l(x)||!addv[x]) return;
 37         addv[l(x)]+=addv[x];
 38         sumv[l(x)]+=addv[x]*(LL)(siz[l(x)]-2*totf[l(x)]);
 39         maxf[l(x)]+=addv[x];
 40         addv[r(x)]+=addv[x];
 41         sumv[r(x)]+=addv[x]*(LL)(siz[r(x)]-2*totf[r(x)]);
 42         maxf[r(x)]+=addv[x];
 43         addv[x]=0;
 44     }
 45
 46     void up(int x){
 47         if(!l(x)) return;
 48         sumv[x]=sumv[l(x)]+sumv[r(x)];
 49         maxf[x]=max(maxf[l(x)],maxf[r(x)]);
 50         totf[x]=totf[l(x)]+totf[r(x)];
 51     }
 52
 53     void change(int x,int l,int r,int ql,int qr,int qv){
 54         push(x);
 55         if(ql<=l&&r<=qr&&maxf[x]+qv<0){
 56             addv[x]+=qv;
 57             sumv[x]+=qv*(LL)(siz[x]-2*totf[x]);
 58             maxf[x]+=qv;
 59         }
 60         else if(l==r){
 61             sumv[x]=qv-sumv[x];
 62             addv[x]=0;
 63             maxf[x]=-INF;
 64             totf[x]=0;
 65         }
 66         else{
 67             int mid=(l+r)>>1;
 68             if(ql<=mid) change(l(x),l,mid,ql,qr,qv);
 69             if(mid<qr)  change(r(x),mid+1,r,ql,qr,qv);
 70             up(x);
 71         }
 72     }
 73
 74     LL ask(int x,int l,int r,int ql,int qr){
 75         push(x);
 76         if(ql<=l&&r<=qr){
 77             return sumv[x];
 78         }
 79         int mid=(l+r)>>1;
 80         LL ans=0;
 81         if(ql<=mid) ans+=ask(l(x),l,mid,ql,qr);
 82         if(mid<qr)  ans+=ask(r(x),mid+1,r,ql,qr);
 83         up(x);
 84         return ans;
 85     }
 86
 87     int build(int src[],int l,int r){
 88         int x=++tot;
 89         siz[x]=r-l+1;
 90         if(l==r){
 91             sumv[x]=Abs(src[l]);
 92             addv[x]=0;
 93             if(src[l]>=0) maxf[x]= -INF;
 94             else maxf[x]=src[l];
 95             totf[x]= (src[l]<0) ? 1:0;
 96             return x;
 97         }
 98         int mid=(l+r)>>1;
 99         l(x)=build(src,l,mid);
100         r(x)=build(src,mid+1,r);
101         up(x);
102         return x;
103     }
104 }
105
106 #define p E[i].x
107
108 int tott;
109 int L[N],a[N],fa[N],pos[N],c[N],d[N],siz[N],h[N],top[N];
110 bool v[N];
111
112 void dfs(int x){
113     v[x]=1; siz[x]=1; h[x]=0;
114     for(int i=g[x];i;i=E[i].to)
115         if(!v[p]){
116             fa[p]=x;
117             d[p]=d[x]+1;
118             dfs(p);
119             siz[x]+=siz[p];
120             if(siz[p]>siz[h[x]])
121                 h[x]=p;
122         }
123 }
124
125 void cut(int x,int ft){
126     L[x]=++tott; c[tott]=a[x]; pos[tott]=x; top[x]=ft;
127     if(h[x]) cut(h[x],ft);
128     for(int i=g[x];i;i=E[i].to)
129         if(p!=fa[x]&&p!=h[x])
130             cut(p,p);
131 }
132
133 void change(int x,int y,int v){
134     int f1=top[x],f2=top[y];
135     while(f1!=f2){
136         if(d[f1]<d[f2]) swap(f1,f2),swap(x,y);
137         Segtree::change(1,1,n,L[f1],L[x],v);
138         x=fa[f1]; f1=top[x];
139     }
140     if(L[x]>L[y]) swap(x,y);
141     Segtree::change(1,1,n,L[x],L[y],v);
142 }
143
144 LL ask(int x,int y){
145     int f1=top[x],f2=top[y];
146     LL ans=0;
147     while(f1!=f2){
148         if(d[f1]<d[f2]) swap(f1,f2),swap(x,y);
149         ans+=Segtree::ask(1,1,n,L[f1],L[x]);
150         x=fa[f1]; f1=top[x];
151     }
152     if(L[x]>L[y]) swap(x,y);
153     ans+=Segtree::ask(1,1,n,L[x],L[y]);
154     return ans;
155 }
156
157 int main(){
158     scanf("%d%d",&n,&m);
159     for(int i=1;i<=n;i++){
160         scanf("%d",&a[i]);
161     }
162     int cmd,x,y;
163     for(int i=1;i<n;i++){
164         scanf("%d%d",&x,&y);
165         ade(x,y); ade(y,x);
166     }
167     d[1]=1;
168     dfs(1); cut(1,1);
169     Segtree::build(c,1,n);
170     for(int i=1;i<=m;i++){
171         int v;
172         scanf("%d%d%d",&cmd,&x,&y);
173         if(cmd==1){
174             scanf("%d",&v);
175             change(x,y,v);
176         }
177         else printf("%lld\n",ask(x,y));
178     }
179     return 0;
180 }

时间: 2024-10-01 05:09:05

BZOJ[4127] Abs的相关文章

BZOJ 4127 Abs 解题报告

这个题感觉很厉害的样子.. 首先我们注意到一点:每次加的 $d$ 都是非负的. 那么就说明一个数只可能从负数变成非负数并且只会变一次. 所以我们就可以暴力地去改变一个数的正负情况. 然后我们就可以用树链剖分,维护一下区间的最大负数和负数的个数就可以了. 时间复杂度 $O(n\log^2 n)$,空间复杂度 $O(n)$. 1 #include <cstdio> 2 typedef long long LL; 3 #define N 262144 + 5 4 #define INF 123456

BZOJ 4127 Abs 树链剖分

题目大意:给定一棵树,每个点有一个整数权值(可以是负数),要求支持两种操作: 1.链上加 2.链上绝对值之和 由于加的数保证非负,因此一个负数变成一个正数最多有n次 树链剖分,在线段树中维护一下区间最大负数即可 不知道为何 写了两个线段树就TLE 把两个线段树合并成一个就7s过了 #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define M 10

bzoj 4127 线段树维护绝对值之和

因为d>=0,所以一个位置的数只会单调不降并且只会有一次穿过0. 用这个性质,我们我可在线段树中记录正数负数的个数和和,以及最大的负数以及答案. 修改操作:如果当前最大负数+d<=0,那么就直接加到懒惰标记中,否则就暴力向下传递. 因为每个节点最多被额外访问该区间负数个数次,所以所有点总共会被额外访问O(nlogn)次,在加上修改操作和询问操作的普通访问O(mlogn)次,所以时间复杂度是有保证的. 谢谢mhy12345的讲解. 1 /*****************************

【BZOJ-4127】Abs 树链剖分 + 线段树 (有趣的姿势)

4127: Abs Time Limit: 40 Sec  Memory Limit: 256 MBSubmit: 381  Solved: 132[Submit][Status][Discuss] Description 给定一棵树,设计数据结构支持以下操作 1 u   v d 表示将路径 (u,v) 加d 2 u v 表示询问路径 (u,v) 上点权绝对值的和 Input 第一行两个整数n和m,表示结点个数和操作数 接下来一行n个整数a_i,表示点i的权值 接下来n-1行,每行两个整数u,v

[BZOJ 1058][ZJOI2007]报表统计

题目:http://www.lydsy.com/JudgeOnline/problem.php?id=1058 题解 方法一:离线+离散化+线段树. 这个方式的常数太大,会T.我在洛谷跑最后两个点TLE了,在BZOJRE了. 具体说一下怎么做吧.首先把所有数离散化,把出现过的绝对值离散化.这样我们就能得到约n+m个数和n+m个绝对值.然后,维护两颗线段树,记录数据是否出现,并记录区间出现的最大值和最小值(因为在线段树上的点是从大到小的,假设我们添加了元素k,然后我们找它的数据排位(下标)),每添

BZOJ 2648: SJY摆棋子

Descrption 平面求最近点...\(n\leqslant 5\times 10^5\) Solution KD-Tree. 双倍经验..BZOJ 2716: [Violet 3]天使玩偶 Code /************************************************************** Problem: 2648 User: BeiYu Language: C++ Result: Accepted Time:13864 ms Memory:32560

【BZOJ】【1021】【SHOI2008】Dept循环的债务

DP 去膜拜题解了>_>玛雅原来是动规…… 让我先理解一下为什么要用动规:这个题根据钱数推方案其实是无从下手的……(线性规划?……事实证明我想多了) 啦-我们先来看个超级简化版的问题:怎么判无法还清?正着判很麻烦对不对= =(其实是我没想……) 那么我们倒着来考虑:有哪些状态是我们通过交换钱币能够到达的,这个可以递推对不>_> 现在我们就知道哪些状态我们是可以到达的了……再多想一下……递推……如果我们依次考虑每种面额的交换策略,顺便也就知道了我们到达这个状态的最小交换次数对吧? 原

【BZOJ】【1520】【POI2006】Szk-Schools

网络流/费用流 比较裸的一道题 依旧是二分图模型,由源点S连向每个学校 i (1,0),「注意是连向第 i 所学校,不是连向学校的标号m[i]……唉这里WA了一次」 然后对于每所学校 i 连接 j+n $(a[i]\leq j \leq b[i])$ 流量为1,费用为 $abs(m[i]-j)*k[i]$ ,最后对于每个标号 j 连边 j+n->T 流量为1费用为0. 跑完费用流以后看流量是否为n,如果不是就说明无解. 1 /***********************************

bzoj 1193 贪心

如果两点的曼哈顿距离在一定范围内时我们直接暴力搜索就可以得到答案,那么开始贪心的跳,判断两点横纵坐标的差值,差值大的方向条2,小的条1,不断做,直到曼哈顿距离较小时可以暴力求解. 备注:开始想的是确定一点周围跳到这个点的答案,然后再枚举周围的点,判断这个点和另一个点的曼哈顿距离,如果能被3整除就说明之前可以一直跳过来,不知道怎么不对. /************************************************************** Problem: 1193 Use