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

因为d>=0,所以一个位置的数只会单调不降并且只会有一次穿过0.

用这个性质,我们我可在线段树中记录正数负数的个数和和,以及最大的负数以及答案.

修改操作:如果当前最大负数+d<=0,那么就直接加到懒惰标记中,否则就暴力向下传递.

因为每个节点最多被额外访问该区间负数个数次,所以所有点总共会被额外访问O(nlogn)次,在加上修改操作和询问操作的普通访问O(mlogn)次,所以时间复杂度是有保证的.

谢谢mhy12345的讲解.

  1 /**************************************************************
  2     Problem: 4127
  3     User: idy002
  4     Language: C++
  5     Result: Accepted
  6     Time:7872 ms
  7     Memory:49256 kb
  8 ****************************************************************/
  9
 10 #include <cstdio>
 11 #include <vector>
 12 #include <algorithm>
 13 #define min(a,b) ((a)<(b)?(a):(b))
 14 #define max(a,b) ((a)>(b)?(a):(b))
 15 #define abs(a) ((a)<0?-(a):(a))
 16 #define oo 0x3f3f3f3f3f3f3f3fLL
 17 #define N 200010
 18 //#define fprintf(...)
 19 using namespace std;
 20
 21 typedef long long dnt;
 22
 23 void getn( int &v ) {
 24     char ch=getchar();
 25     bool neg = false;
 26     while( ch!=‘-‘ && (ch<‘0‘ || ch>‘9‘) ) ch=getchar();
 27     if( ch==‘-‘ ) {
 28         neg = true;
 29         ch = getchar();
 30     }
 31     v = ch-‘0‘;
 32     ch = getchar();
 33     while( ‘0‘<=ch && ch<=‘9‘ ) {
 34         v = v*10+ch-‘0‘;
 35         ch = getchar();
 36     }
 37     if( neg ) v=-v;
 38 }
 39
 40 struct Node {
 41     dnt ans, sum[2], cnt[2], nmx, tag;
 42     Node *ls, *rs;
 43     inline void init( int v ) {
 44         if( v<0 ) {
 45             sum[0] = v;
 46             cnt[0] = 1;
 47             sum[1] = 0;
 48             cnt[1] = 0;
 49             ans = -v;
 50             tag = 0;
 51             nmx = v;
 52         } else {
 53             sum[0] = 0;
 54             cnt[0] = 0;
 55             sum[1] = v;
 56             cnt[1] = 1;
 57             tag = 0;
 58             ans = v;
 59             nmx = -oo;
 60         }
 61     }
 62     inline void update() {
 63         sum[0] = ls->sum[0] + rs->sum[0];
 64         sum[1] = ls->sum[1] + rs->sum[1];
 65         cnt[0] = ls->cnt[0] + rs->cnt[0];
 66         cnt[1] = ls->cnt[1] + rs->cnt[1];
 67         ans = ls->ans + rs->ans;
 68         nmx = max( ls->nmx, rs->nmx );
 69     }
 70     inline void pushdown() {
 71         if( tag ) {
 72             ls->nmx += tag;
 73             rs->nmx += tag;
 74             ls->sum[0] += ls->cnt[0]*tag;
 75             ls->sum[1] += ls->cnt[1]*tag;
 76             rs->sum[0] += rs->cnt[0]*tag;
 77             rs->sum[1] += rs->cnt[1]*tag;
 78             ls->ans = ls->sum[1]-ls->sum[0];
 79             rs->ans = rs->sum[1]-rs->sum[0];
 80             ls->tag += tag;
 81             rs->tag += tag;
 82             tag = 0;
 83         }
 84     }
 85     void modify( int lf, int rg, int L, int R, int delta ) {
 86         if( lf==rg ) {
 87             if( cnt[0] ) {
 88                 if( sum[0]+delta<=0 ) {
 89                     sum[0]+=delta;
 90                     nmx = sum[0];
 91                     ans = -sum[0];
 92                 } else {
 93                     sum[1] = sum[0]+delta;
 94                     ans = sum[1];
 95                     cnt[1] = 1;
 96                     sum[0] = cnt[0] = 0;
 97                     nmx = -oo;
 98                 }
 99             } else {
100                 sum[1]+=delta;
101                 ans = sum[1];
102             }
103             return;
104         }
105         if( L<=lf && rg<=R && nmx+delta<=0 ) {
106             nmx += delta;
107             sum[0] += cnt[0]*delta;
108             sum[1] += cnt[1]*delta;
109             ans = sum[1]-sum[0];
110             tag += delta;
111             return;
112         }
113         pushdown();
114         int mid=(lf+rg)>>1;
115         if( L<=mid ) ls->modify(lf,mid,L,R,delta);
116         if( R>mid ) rs->modify(mid+1,rg,L,R,delta);
117         update();
118     }
119     dnt query( int lf, int rg, int L, int R ) {
120         if( L<=lf && rg<=R ) return ans;
121         pushdown();
122         int mid=(lf+rg)>>1;
123         dnt rt = 0;
124         if( L<=mid ) rt+=ls->query(lf,mid,L,R);
125         if( R>mid ) rt+=rs->query(mid+1,rg,L,R);
126         update();
127         return rt;
128     }
129 }pool[N*3], *tail=pool, *root;
130
131 int n, m;
132 int aa[N], bb[N];
133 int head[N], dest[N<<1], next[N<<1], etot;
134 int fat[N], siz[N], son[N], top[N], dep[N], vid[N], qu[N], bg, ed;
135
136 void adde( int u, int v ) {
137     etot++;
138     dest[etot] = v;
139     next[etot] = head[u];
140     head[u] = etot;
141 }
142 Node *build( int lf, int rg ) {
143     Node *nd = ++tail;
144     if( lf==rg) {
145         nd->init(bb[lf]);
146         return nd;
147     }
148     int mid=(lf+rg)>>1;
149     nd->ls = build( lf, mid );
150     nd->rs = build( mid+1, rg );
151     nd->update();
152     return nd;
153 }
154 void build_dcp( int s ) {
155     //  fat dep
156     fat[s] = 0;
157     dep[s] = 1;
158     qu[bg=ed=1] = s;
159     while( bg<=ed ) {
160         int u=qu[bg++];
161         for( int t=head[u]; t; t=next[t] ) {
162             int v=dest[t];
163             if( v==fat[u] ) continue;
164             fat[v] = u;
165             dep[v] = dep[u]+1;
166             qu[++ed] = v;
167         }
168     }
169     //  siz son
170     for( int i=ed; i>=1; i-- ) {
171         int u=qu[i], p=fat[u];
172         siz[u]++;
173         if( p ) {
174             siz[p] += siz[u];
175             if( siz[son[p]]<siz[u] ) son[p]=u;
176         }
177     }
178     //  vid top
179     vid[s] = 1;
180     top[s] = s;
181     for( int i=1; i<=ed; i++ ) {
182         int u=qu[i];
183         int cur=vid[u]+1;
184         if( son[u] ) {
185             vid[son[u]] = cur;
186             top[son[u]] = top[u];
187             cur += siz[son[u]];
188         }
189         for( int t=head[u]; t; t=next[t] ) {
190             int v=dest[t];
191             if( v==fat[u] || v==son[u] ) continue;
192             vid[v] = cur;
193             top[v] = v;
194             cur += siz[v];
195         }
196     }
197     //  segment tree
198     for( int i=1; i<=n; i++ )
199         bb[vid[i]] = aa[i];
200 //  for( int i=1; i<=n; i++ )
201 //      fprintf( stderr, "%d ", bb[i] );
202 //  fprintf( stderr, "\n" );
203     root = build( 1, n );
204 }
205 void modify( int u, int v, int delta ) {
206     while( top[u]!=top[v] ) {
207         if( dep[top[u]]<dep[top[v]] ) swap(u,v);
208         root->modify(1,n,vid[top[u]],vid[u],delta);
209 //      fprintf( stderr, "modify( %d %d %d )\n", vid[top[u]], vid[u], delta );
210         u=fat[top[u]];
211     }
212     if( dep[u]<dep[v] ) swap(u,v);
213     root->modify(1,n,vid[v],vid[u],delta);
214 //  fprintf( stderr, "modify( %d %d %d )\n", vid[v], vid[u], delta );
215 }
216 dnt query( int u, int v ) {
217     dnt rt = 0;
218     while( top[u]!=top[v] ) {
219         if( dep[top[u]]<dep[top[v]] ) swap(u,v);
220         rt += root->query(1,n,vid[top[u]],vid[u]);
221 //      fprintf( stderr, "query( %d %d ) rt = %lld\n", vid[top[u]], vid[u], rt );
222         u=fat[top[u]];
223     }
224     if( dep[u]<dep[v] ) swap(u,v);
225     rt += root->query(1,n,vid[v],vid[u]);
226 //  fprintf( stderr, "query( %d %d ) rt = %lld\n", vid[v], vid[u], rt );
227     return rt;
228 }
229 int main() {
230     getn(n); getn(m);
231     for( int i=1; i<=n; i++ )
232         getn(aa[i]);
233     for( int i=1,u,v; i<n; i++ ) {
234         getn(u); getn(v);
235         adde( u, v );
236         adde( v, u );
237     }
238     build_dcp(1);
239     for( int t=1,opt,u,v,d; t<=m; t++ ) {
240         getn(opt);
241         if( opt==1 ) {
242             getn(u);getn(v);getn(d);
243             modify( u, v, d );
244         } else {
245             getn(u); getn(v);
246             printf( "%lld\n", query(u,v) );
247         }
248     }
249 }

时间: 2024-08-29 03:13:33

bzoj 4127 线段树维护绝对值之和的相关文章

bzoj 1018 线段树维护连通性

本题将一道LCT的题特殊化(支持加边和删边,询问图的连通性),将图变成了2×m的网格图,然后就神奇地可以用线段树来维护. 对于每个区间[l,r],维护其四个角落之间的连通性(仅仅通过[l,r]这段的边构建起的连通性). 查询[l,r]时,先计算出[1,l-1],[l,r],[r+1,c]这三个线段的连通性,然后将[l,r]的四个角变成并查集的4个点,先用[l,r]中的6种关系更新,在看是否可以从左上角的点通过左边区间绕道左下角,以及从右上角通过右边区间绕道右下角,该并的并起来后直接看查询的点是否

BZOJ 1018 线段树维护图的连通性问题

思路: 我们可以搞一棵线段树 对于一段区间有6种情况需要讨论 左上右下.左上右上.左下右下.左下右上 这四种比较好维护 用左上右下举个例子吧 就是左儿子的左上右下&左区间到右区间下面有路&右儿子的左下右下 或者是左儿子的左上右上&左区间到右区间上面有路&右儿子的左上右下 还有两种  区间的左(右)端点上下能不能联通 需要维护 这种就是左儿子的上下连通或(左上右上&左上右下&左到右两条路都联通&右儿子的上下联通) (假设c1<c2) 最后要查的是

BZOJ 1018 线段树维护图连通性

用8个bool维护即可分别为LURU,LURD,LDRU,LDRD,LULD,RURD,Side[1],Side[2]即可. Side表示这一块有没有接到右边.Merge一下就可以了.码农题,WA了一次,发现未初始化,就AC了.. 1 #include <cstdio> 2 inline int Min(int x,int y) {return x>y?y:x;} 3 inline void Swap(int &x,int &y) {int t=x;x=y;y=t;} 4

[BZOJ 1018] [SHOI2008] 堵塞的交通traffic 【线段树维护联通性】

题目链接:BZOJ - 1018 题目分析 这道题就说明了刷题少,比赛就容易跪..SDOI Round1 Day2 T3 就是与这道题类似的..然而我并没有做过这道题.. 这道题是线段树维护联通性的经典模型. 我们线段树的一个节点表示一个区间的联通性,有 6 个 bool 值,表示这个区间的 4 个角上的点之间的联通性. 然后用两个子区间的联通性和两个子区间之间的连边情况合并出整个区间的联通性. 修改某条边时,先在边的数组中修改,然后从这条边所在的点的线段树叶子开始向上 Update . 询问两

[BZOJ 3995] [SDOI2015] 道路修建 【线段树维护连通性】

题目链接:BZOJ - 3995 题目分析 这道题..是我悲伤的回忆.. 线段树维护连通性,与 BZOJ-1018 类似,然而我省选之前并没有做过  1018,即使它在 ProblemSet 的第一页. 更悲伤的是,这道题有 40 分的暴力分,写个 Kruskal 就可以得到,然而我写了个更快的 DP . 这本来没有什么问题,然而我的 DP 转移少些了一种情况,于是...爆零.没错,省选前20名可能就我没有得到这 40 分? 不想再多说什么了...希望以后不要再这样 SB 了,如果以后还有机会的

BZOJ 3779 重组病毒 LCT+线段树维护DFS序

题目大意:给定一棵树,初始每个点都有一个颜色,支持三种操作: 1.将某个点到根的路径上所有点染上一种新的颜色 2.将某个点到根的路径上所有点染上一种新的颜色,然后把根设为这个点 3.定义一个点的代价为这个点到根路径上颜色的种类数,求某个点子树中所有点代价的平均值 我真是炖了狗了-- 容易发现这玩应就是个LCT,操作1就是Access,操作2就是Move_To_Root,代价就是一个点到根路径上的虚边数量+1 我们用LCT模拟上述操作,用线段树维护DFS序维护信息,一旦LCT中出现了虚实边的切换,

BZOJ 2402 陶陶的难题II 二分答案+斜率优化+树链剖分+线段树维护凸包

题目大意:给定一棵树,每个点有两个坐标(x1,y1)和(x2,y2),多次询问某条链上选择两个点i和j(可以相同),求(y1i+y2j)/(x1i+x2j)的最大值 我竟没看出来这是01分数规划...真是老了... 二分答案ans,问题转化成验证(y1i+y2j)/(x1i+x2j)是否>=ans 将式子变形可得(y1i-ans*x1i)+(y2j-ans*x2j)>=0 加号两边独立,分别计算即可 问题转化为求链上y-ans*x最大的点 令P=y-ans*x 则y=ans*x+P 我们发现这

BZOJ 2124: 等差子序列 线段树维护hash

2124: 等差子序列 Description 给一个1到N的排列{Ai},询问是否存在1<=p1=3),使得Ap1,Ap2,Ap3,…ApLen是一个等差序列. Input 输入的第一行包含一个整数T,表示组数.下接T组数据,每组第一行一个整数N,每组第二行为一个1到N的排列,数字两两之间用空格隔开. Output 对于每组数据,如果存在一个等差子序列,则输出一行“Y”,否则输出一行“N”. Sample Input 2 3 1 3 2 3 3 2 1 Sample Output N Y HI

从《楼房重建》出发浅谈一类使用线段树维护前缀最大值的算法

首先需要申明的是,真的是浅谈,因为我对这个算法的认识还是非常低的. 既然是从<楼房重建>出发,那么当然是先看看这道题: [清华集训2013]楼房重建 bzoj 链接 题意简述: 有 \(n\) 栋楼,第 \(i\) 栋的高度为 \(H_i\),也就是说第 \(i\) 栋楼可以抽象成一条两端点为 \((i, 0)\) 和 \((i, H_i)\) 的线段. 初始时 \(H_i\) 均为 \(0\),要支持动态修改单点的 \(H_i\). 每次询问从 \(O(0, 0)\) 点可以看到多少栋楼房.