bzoj 3999 线段树区间提取 有序链剖

看错题目了,想成每个城市都可以买一个东西,然后在后面的某个城市卖掉,问最大收益.这个可以类似维护上升序列的方法在O(nlog^3n)的时间复杂度内搞定

这道题用到的一些方法:

  1. 可以将有关的线段提取出来,然后一起处理.

  2. 线段树可以维护两个方向的信息,这样就可以处理树上有序的东西.

  1 /**************************************************************
  2     Problem: 3999
  3     User: idy002
  4     Language: C++
  5     Result: Accepted
  6     Time:3204 ms
  7     Memory:12144 kb
  8 ****************************************************************/
  9
 10 #include <cstdio>
 11 #include <cstring>
 12 #include <algorithm>
 13 #define max(a,b) ((a)>(b)?(a):(b))
 14 #define N 50010
 15 using namespace std;
 16
 17 typedef long long dnt;
 18 struct Node {
 19     dnt t, b, a[2], tag;
 20     int lf, rg;
 21     Node *ls, *rs;
 22     void pushdown() {
 23         if( tag ) {
 24             ls->t += tag;
 25             ls->b += tag;
 26             ls->tag += tag;
 27             rs->t += tag;
 28             rs->b += tag;
 29             rs->tag += tag;
 30             tag = 0;
 31         }
 32     }
 33     void update() {
 34         t = max( ls->t, rs->t );
 35         b = min( ls->b, rs->b );
 36         a[0] = max( ls->a[0], rs->a[0] );
 37         a[0] = max( a[0], rs->t-ls->b );
 38         a[1] = max( ls->a[1], rs->a[1] );
 39         a[1] = max( a[1], ls->t-rs->b );
 40     }
 41     void modify( int L, int R, int d ) {
 42         if( L<=lf && rg<=R ) {
 43             t += d;
 44             b += d;
 45             tag += d;
 46             return;
 47         }
 48         pushdown();
 49         int mid=(lf+rg)>>1;
 50         if( L<=mid ) ls->modify( L, R, d );
 51         if( R>mid ) rs->modify( L, R, d );
 52         update();
 53     }
 54 }pool[N*3], *tail=pool, *root;
 55
 56 int n, m;
 57 int head[N], dest[N<<1], next[N<<1], etot;
 58 int aa[N], bb[N];
 59 int fat[N], dep[N], siz[N], son[N], top[N], vid[N];
 60 int qu[N], bg, ed;
 61 Node *su[N], *sv[N];
 62 int tu, tv;
 63
 64 void adde( int u, int v ) {
 65     etot++;
 66     dest[etot] = v;
 67     next[etot] = head[u];
 68     head[u] = etot;
 69 }
 70 Node *build( int lf, int rg ) {
 71     Node *nd = ++tail;
 72     if( lf==rg ) {
 73         nd->b = nd->t = bb[lf];
 74         nd->a[0] = nd->a[1] = 0;
 75         nd->lf=lf, nd->rg=rg;
 76     } else {
 77         int mid=(lf+rg)>>1;
 78         nd->ls = build( lf, mid );
 79         nd->rs = build( mid+1, rg );
 80         nd->lf=lf, nd->rg=rg;
 81         nd->update();
 82     }
 83     return nd;
 84 }
 85 void fetch( Node *nd, int L, int R, Node *(&stk)[N], int &top ) {
 86     int lf=nd->lf, rg=nd->rg;
 87     if( L<=lf && rg<=R ) {
 88         stk[++top] = nd;
 89         return;
 90     }
 91     int mid=(lf+rg)>>1;
 92     nd->pushdown();
 93     if( R>mid ) fetch(nd->rs,L,R,stk,top);
 94     if( L<=mid ) fetch(nd->ls,L,R,stk,top);
 95 }
 96 void build_dcp( int s ) {
 97     //  fat dep
 98     fat[s] = 0;
 99     dep[s] = 1;
100     qu[bg=ed=1] = s;
101     while( bg<=ed ) {
102         int u=qu[bg++];
103         for( int t=head[u]; t; t=next[t] ) {
104             int v=dest[t];
105             if( v==fat[u] ) continue;
106             fat[v] = u;
107             dep[v] = dep[u] + 1;
108             qu[++ed] = v;
109         }
110     }
111     //  siz son
112     for( int i=ed; i>=1; i-- ) {
113         int u=qu[i], p=fat[u];
114         siz[u]++;
115         if( p ) {
116             siz[p] += siz[u];
117             if( siz[u]>siz[son[p]] ) son[p]=u;
118         }
119     }
120     //  top vid
121     top[s] = s;
122     vid[s] = 1;
123     for( int i=1; i<=ed; i++ ) {
124         int u=qu[i];
125         int cur=vid[u]+1;
126         if( son[u] ) {
127             top[son[u]] = top[u];
128             vid[son[u]] = cur;
129             cur += siz[son[u]];
130         }
131         for( int t=head[u]; t; t=next[t] ) {
132             int v=dest[t];
133             if( v==fat[u] || v==son[u] ) continue;
134             top[v] = v;
135             vid[v] = cur;
136             cur += siz[v];
137         }
138     }
139     //  segment
140     for( int i=1; i<=n; i++ )
141         bb[vid[i]] = aa[i];
142     root = build( 1, n );
143 }
144 int lca( int u, int v ) {
145     while( top[u]!=top[v] ) {
146         if( dep[top[u]]<dep[top[v]] ) swap(u,v);
147         u = fat[top[u]];
148     }
149     return dep[u]<dep[v] ? u : v;
150 }
151 dnt query( int u, int v ) {
152     if( u==v ) return 0;
153     int ca = lca(u,v);
154     tu = tv = 0;
155     while( top[u]!=top[ca] ) {
156         fetch(root,vid[top[u]],vid[u],su,tu);
157         u=fat[top[u]];
158     }
159     while( top[v]!=top[ca] ) {
160         fetch(root,vid[top[v]],vid[v],sv,tv);
161         v=fat[top[v]];
162     }
163     if( u!=ca )
164         fetch(root,vid[ca],vid[u],su,tu);
165     else
166         fetch(root,vid[ca],vid[v],sv,tv);
167     dnt curt = 0;
168     dnt rt = 0;
169     for( int i=1; i<=tv; i++ ) {
170         rt = max( rt, sv[i]->a[0] );
171         rt = max( rt, curt-sv[i]->b );
172         curt = max( curt, sv[i]->t );
173     }
174     for( int i=tu; i>=1; i-- ) {
175         rt = max( rt, su[i]->a[1] );
176         rt = max( rt, curt-su[i]->b );
177         curt = max( curt, su[i]->t );
178     }
179     return rt;
180 }
181 void modify( int u, int v, int d ) {
182     while( top[u]!=top[v] ) {
183         if( dep[top[u]]<dep[top[v]] ) swap(u,v);
184         root->modify(vid[top[u]],vid[u],d);
185         u=fat[top[u]];
186     }
187     if( dep[u]<dep[v] ) swap(u,v);
188     root->modify(vid[v],vid[u],d);
189 }
190 int main() {
191     scanf( "%d", &n );
192     for( int i=1; i<=n; i++ )
193         scanf( "%d", aa+i );
194     for( int i=1,u,v; i<n; i++ ) {
195         scanf( "%d%d", &u, &v );
196         adde( u, v );
197         adde( v, u );
198     }
199     build_dcp(1);
200     scanf( "%d", &m );
201     for( int t=1,u,v,d; t<=m; t++ ) {
202         scanf( "%d%d%d", &u, &v, &d );
203         printf( "%lld\n", query(u,v) );
204         modify(u,v,d);
205     }
206 }

时间: 2024-10-15 06:21:52

bzoj 3999 线段树区间提取 有序链剖的相关文章

Hdu 3966 Aragorn&#39;s Story (树链剖分 + 线段树区间更新)

题目链接: Hdu 3966 Aragorn's Story 题目描述: 给出一个树,每个节点都有一个权值,有三种操作: 1:( I, i, j, x ) 从i到j的路径上经过的节点全部都加上x: 2:( D, i, j, x ) 从i到j的路径上经过的节点全部都减去x: 3:(Q, x) 查询节点x的权值为多少? 解题思路: 可以用树链剖分对节点进行hash,然后用线段树维护(修改,查询),数据范围比较大,要对线段树进行区间更新 1 #include <cstdio> 2 #include

【bzoj4811】[Ynoi2017]由乃的OJ 树链剖分+线段树区间合并

题目描述 由乃正在做她的OJ.现在她在处理OJ上的用户排名问题.OJ上注册了n个用户,编号为1-",一开始他们按照编号 排名.由乃会按照心情对这些用户做以下四种操作,修改用户的排名和编号:然而由乃心情非常不好,因为Deus天 天问她题...因为Deus天天问由乃OI题,所以由乃去学习了一下OI,由于由乃智商挺高,所以OI学的特别熟练她 在RBOI2016中以第一名的成绩进入省队,参加了NOI2016获得了金牌保送 Deus:这个题怎么做呀? yuno:这个不是NOI2014的水题吗... Deu

HDU 1689 Just a Hook 线段树区间更新求和

点击打开链接 Just a Hook Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 18894    Accepted Submission(s): 9483 Problem Description In the game of DotA, Pudge's meat hook is actually the most horrible

HDU 3911 Black And White(线段树区间合并)

Problem Description There are a bunch of stones on the beach; Stone color is white or black. Little Sheep has a magic brush, she can change the color of a continuous stone, black to white, white to black. Little Sheep like black very much, so she wan

HDU4027 Can you answer these queries 线段树区间求和+剪枝

给了你n,然后n个数字在一个数组中,接下来m个询问,每个询问三个数字 t,x,y,若t==0,那么修改区间[x,y]的每一个值,变为原来每个位置上的数 开根号取整,若t==1,那么对区间[x,y]求和 由于n,m,很大,所以树状数组铁定超时,若直接用线段树来做区间修改,那么也是超时,这类题目没别的方法了,静心剪枝,发现题目给的数据范围为2^63,有没有发现,2^63开根号 绝对不需要开10次,就能到1,到1以后就不需要再开了,意思就是若有某个区间[x,y]每一个点的值都为1时,这一段区间事实上是

POJ 3667 线段树区间合并

http://www.cnblogs.com/scau20110726/archive/2013/05/07/3065418.html 用线段树,首先要定义好线段树的节点信息,一般看到一个问题,很难很快能确定线段树要记录的信息做线段树不能为了做题而做,首先线段树是一种辅助结构,它是为问题而生的,因而必须具体问题具体分析回忆一下RMQ问题,其实解决RMQ有很多方法,根本不需要用到线段树,用线段树解决RMQ,其实是利用线段树的性质来辅助解决这个问题回忆一下求矩形面积并或周长并的问题,一般使用的是扫描

HDU 3308 LCIS (线段树区间合并)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3308 题目很好懂,就是单点更新,然后求区间的最长上升子序列. 线段树区间合并问题,注意合并的条件是a[mid + 1] > a[mid],写的细心点就好了. 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 using namespace std; 5 const int MAXN = 1

POJ 2528 Mayor&#39;s posters (线段树区间更新+离散化)

题目链接:http://poj.org/problem?id=2528 给你n块木板,每块木板有起始和终点,按顺序放置,问最终能看到几块木板. 很明显的线段树区间更新问题,每次放置木板就更新区间里的值.由于l和r范围比较大,内存就不够了,所以就用离散化的技巧 比如将1 4化为1 2,范围缩小,但是不影响答案. 写了这题之后对区间更新的理解有点加深了,重点在覆盖的理解(更新左右两个孩子节点,然后值清空),还是要多做做题目. 1 #include <iostream> 2 #include <

HDU 5023 A Corrupt Mayor&#39;s Performance Art 线段树区间更新+状态压缩

Link:  http://acm.hdu.edu.cn/showproblem.php?pid=5023 1 #include <cstdio> 2 #include <cstring> 3 #include <iostream> 4 #include <algorithm> 5 #include <vector> 6 #include <string> 7 #include <cmath> 8 using namesp