[luogu]月下“毛景树”:树 剖 好 题

原题



题意:

给一棵树 , 每一条边都有边权 , 支持四种操作

1. 将第k条边权值修改为k

2. 将节点u到v的边权都覆盖为k

3. 将节点u到v的边权都增加k

4. 询问区间边权最大值



显然树剖

边权转点权然后就是树剖裸题了

怎样边权转点权



对于一个点u , 她的父亲是 v

将 u->v 的边权放到 u 上做点权

然后树剖最后的时候这样操作一波

1     if(a == b)
2         return;
3     if(dep[a] > dep[b])
4         std::swap(a, b);
5     SgCover(1, id[a] + 1, id[b], k);

这样就保证了这两个点的 LCA 的点权不会被统计(LCA的点权指向的是 LCA->fa[LCA]  , 很显然不能统计)



另一个问题 , 线段树的lazytag

我维护了两个懒标记

一个是 cov , 是覆盖的懒标记

一个是 add , 是增加的懒标记

至于具体用法还是看代码吧 ... 挺好理解的



代码:

  1 #include<cmath>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<cstdlib>
  5 #include<iostream>
  6 #include<algorithm>
  7 #define APART puts("----------------------")
  8 #define debug 1
  9 #define FILETEST 1
 10 #define inf 100010
 11 #define ll long long
 12 #define ha 998244353
 13 #define INF 0x7fffffff
 14 #define INF_T 9223372036854775807
 15 #define DEBUG printf("%s %d\n",__FUNCTION__,__LINE__)
 16
 17 namespace chino{
 18
 19 inline void setting(){
 20 #if FILETEST
 21     freopen("_test.in", "r", stdin);
 22     freopen("_test.me.out", "w", stdout);
 23 #endif
 24     return;
 25 }
 26
 27 inline int read(){
 28     char c = getchar(), up = c; int num = 0;
 29     for(; c < ‘0‘ || c > ‘9‘; up = c, c = getchar());
 30     for(; c >= ‘0‘ && c <= ‘9‘; num = (num << 3) + (num << 1) + (c ^ ‘0‘), c = getchar());
 31     return  up == ‘-‘ ? -num : num;
 32 }
 33
 34 int n;
 35 int cntE, cntT;
 36 char s[inf];
 37 int mem[inf];
 38 int top[inf];
 39 int dep[inf];
 40 int son[inf];
 41 int fa[inf];
 42 int size[inf];
 43 int id[inf];
 44 int val[inf];
 45 int head[inf << 1];
 46 struct Edge{
 47     int to;
 48     int val;
 49     int next;
 50 }e[inf << 1];
 51 struct Tree{
 52     int l;
 53     int r;
 54     int max;
 55     int cov;
 56     int add;
 57 }t[inf << 2];
 58 std::pair <int, int> p[inf];
 59 #define lson(a) (a) << 1
 60 #define rson(a) (a) << 1 | 1
 61
 62 inline void AddEdge(int from, int to, int val){
 63     ++cntE;
 64     e[cntE].to = to;
 65     e[cntE].val = val;
 66     e[cntE].next = head[from];
 67     head[from] = cntE;
 68     return;
 69 }
 70
 71 inline int getVal(int now){
 72     if(mem[now])
 73         return mem[now];
 74     for(int i = head[now]; i; i = e[i].next){
 75         if(fa[now] == e[i].to)
 76             return mem[now] = e[i].val;
 77     }
 78     return mem[now] = -INF;
 79 }
 80
 81 void Dfs1(int now, int f, int depth){
 82     fa[now] = f;
 83     dep[now] = depth;
 84     size[now] = 1;
 85     int heavy = -INF;
 86     for(int i = head[now]; i; i = e[i].next){
 87         int to = e[i].to;
 88         if(to == f)
 89             continue;
 90         Dfs1(to, now, depth + 1);
 91         int y = size[to];
 92         size[now] += y;
 93         if(y > heavy){
 94             heavy = y;
 95             son[now] = to;
 96         }
 97     }
 98     return;
 99 }
100
101 void Dfs2(int now, int point){
102     ++cntT;
103     top[now] = point;
104     id[now] = cntT;
105     val[cntT] = (now == 1 ? -INF : getVal(now));
106     if(son[now] == 0)
107         return;
108     Dfs2(son[now], point);
109     for(int i = head[now]; i; i = e[i].next){
110         int to = e[i].to;
111         if(to == fa[now] || to == son[now])
112             continue;
113         Dfs2(to, to);
114     }
115     return;
116 }
117
118 using std::max;
119
120 inline void updata(int now){
121     t[now].max = max (t[lson(now)].max , t[rson(now)].max);
122     return;
123 }
124
125 inline void pushtag(int now){
126     if(t[now].cov != -INF){
127         t[lson(now)].max = t[now].cov;
128         t[rson(now)].max = t[now].cov;
129         t[lson(now)].cov = t[now].cov;
130         t[rson(now)].cov = t[now].cov;
131         t[lson(now)].add = 0;
132         t[rson(now)].add = 0;
133         t[now].cov = -INF;
134     }
135     if(t[now].add != 0){
136         t[lson(now)].max += t[now].add;
137         t[rson(now)].max += t[now].add;
138         t[lson(now)].add += t[now].add;
139         t[rson(now)].add += t[now].add;
140         t[now].add = 0;
141     }
142     return;
143 }
144
145 void build(int now, int l, int r){
146     t[now].l = l;
147     t[now].r = r;
148     t[now].cov = -INF;
149     if(l == r){
150         t[now].max = val[l];
151         return;
152     }
153     int mid = (l + r) >> 1;
154     build(lson(now), l, mid);
155     build(rson(now), mid + 1, r);
156     updata(now);
157     return;
158 }
159 void SgCover(int now, int l, int r, int k){
160     if(t[now].l >= l && t[now].r <= r){
161         t[now].cov = t[now].max = k;
162         t[now].add = 0;
163         return;
164     }
165     pushtag(now);
166     int mid = (t[now].l + t[now].r) >> 1;
167     if(l <= mid)
168         SgCover(lson(now), l, r, k);
169     if(r > mid)
170         SgCover(rson(now), l, r, k);
171     updata(now);
172     return;
173 }
174
175 void SgAdd(int now, int l, int r, int k){
176     if(t[now].l >= l && t[now].r <= r){
177         t[now].add += k;
178         t[now].max += k;
179         return;
180     }
181     pushtag(now);
182     int mid = (t[now].l + t[now].r) >> 1;
183     if(l <= mid)
184         SgAdd(lson(now), l, r, k);
185     if(r > mid)
186         SgAdd(rson(now), l, r, k);
187     updata(now);
188     return;
189 }
190
191 void SgChange(int now, int x, int k){
192     if(t[now].l > x || t[now].r < x)
193         return;
194     if(t[now].l == t[now].r){
195         t[now].max = k;
196         t[now].cov = -INF;
197         t[now].add = 0;
198         return;
199     }
200     pushtag(now);
201     int mid = (t[now].l + t[now].r) >> 1;
202     if(x <= mid)
203         SgChange(lson(now), x, k);
204     else
205         SgChange(rson(now), x, k);
206     updata(now);
207     return;
208 }
209
210 int SgQuery(int now, int l, int r){
211     if(t[now].l >= l && t[now].r <= r)
212         return t[now].max;
213     pushtag(now);
214     int mid = (t[now].l + t[now].r) >> 1;
215     int Max = -INF;
216     if(l <= mid)
217         Max = max (Max, SgQuery(lson(now), l, r));
218     if(r > mid)
219         Max = max (Max, SgQuery(rson(now), l, r));
220     return Max;
221 }
222
223 inline void cover(int a, int b, int k){
224     while(top[a] != top[b]){
225         if(dep[top[a]] < dep[top[b]])
226             std::swap(a, b);
227         SgCover(1, id[top[a]], id[a], k);
228         a = fa[top[a]];
229     }
230     if(a == b)
231         return;
232     if(dep[a] > dep[b])
233         std::swap(a, b);
234     SgCover(1, id[a] + 1, id[b], k);
235     return;
236 }
237
238 inline void add(int a, int b, int k){
239     while(top[a] != top[b]){
240         if(dep[top[a]] < dep[top[b]])
241             std::swap(a, b);
242         SgAdd(1, id[top[a]], id[a], k);
243         a = fa[top[a]];
244     }
245     if(a == b)
246         return;
247     if(dep[a] > dep[b])
248         std::swap(a, b);
249     SgAdd(1, id[a] + 1, id[b], k);
250     return;
251 }
252
253 inline int query(int a, int b){
254     int ans = -INF;
255     while(top[a] != top[b]){
256         if(dep[top[a]] < dep[top[b]])
257             std::swap(a, b);
258         ans = max (ans, SgQuery(1, id[top[a]], id[a]));
259         a = fa[top[a]];
260     }
261     if(a == b)
262         return ans;
263     if(dep[a] > dep[b])
264         std::swap(a, b);
265     ans = max (ans, SgQuery(1, id[a] + 1, id[b]));
266     return ans;
267 }
268
269 inline int main(){
270     n = read();
271     for(int i = 1; i < n; i++){
272         p[i].first  = read();
273         p[i].second = read();
274         int w = read();
275         AddEdge(p[i].first, p[i].second, w);
276         AddEdge(p[i].second, p[i].first, w);
277     }
278     Dfs1(1, 0, 1);
279     Dfs2(1, 1);
280     build(1, 1, n);
281     while(true){
282         scanf("%s", s + 1);
283         if(s[1] == ‘S‘)
284             break;
285         int a = read();
286         int b = read();
287         if(s[2] == ‘a‘){
288             int ans = query(a, b);
289             printf("%d\n", ans == -INF ? 0 : ans);
290         } else if(s[2] == ‘o‘)
291             cover(a, b, read());
292         else if(s[2] == ‘h‘){
293             if(fa[p[a].first] == p[a].second)
294                 SgChange(1, id[p[a].first], b);
295             else
296                 SgChange(1, id[p[a].second],b);
297         } else
298             add(a, b, read());
299     }
300     return 0;
301 }
302
303 }//namespace chino
304
305 signed main(){return chino::main();}

附:这题好难写啊 .. 可能是因为我太菜了 .. 很久以前就写过 ... 然后挂了0pts ... 实在不想调一个 350 行的程序 ... 后来今天重构了一遍 ... 不过还是依靠对拍 debug 了好几个小时 ... 码力不行啊 ...

原文地址:https://www.cnblogs.com/chiarochinoful/p/problem-luogu-P4315.html

时间: 2024-10-11 07:44:19

[luogu]月下“毛景树”:树 剖 好 题的相关文章

[luogu4315]月下“毛景树”

[luogu4315]月下"毛景树" luogu 联赛前复习一发树剖.不会告诉你WA了4发 #define ls x<<1,l,mid #define rs x<<1|1,mid+1,r #include<bits/stdc++.h> using namespace std; const int _=1e5+5; int re(){ int x=0,w=1;char ch=getchar(); while(ch<'0'||ch>'9'){

【BZOJ1984】月下“毛景树” 树链剖分+线段树

[BZOJ1984]月下"毛景树" Description 毛毛虫经过及时的变形,最终逃过的一劫,离开了菜妈的菜园. 毛毛虫经过千山万水,历尽千辛万苦,最后来到了小小的绍兴一中的校园里.爬啊爬~爬啊爬~~毛毛虫爬到了一颗小小的"毛景树"下面,发现树上长着他最爱吃的毛毛果~~~ "毛景树"上有N个节点和N-1条树枝,但节点上是没有毛毛果的,毛毛果都是长在树枝上的.但是这棵"毛景树"有着神奇的魔力,他能改变树枝上毛毛果的个数: ?

P4315 月下“毛景树”

P4315 月下"毛景树" 题目描述 毛毛虫经过及时的变形,最终逃过的一劫,离开了菜妈的菜园. 毛毛虫经过千山万水,历尽千辛万苦,最后来到了小小的绍兴一中的校园里. 爬啊爬~爬啊爬毛毛虫爬到了一颗小小的"毛景树"下面,发现树上长着他最爱吃的毛毛果~ "毛景树"上有N个节点和N-1条树枝,但节点上是没有毛毛果的,毛毛果都是长在树枝上的.但是这棵"毛景树"有着神奇的魔力,他能改变树枝上毛毛果的个数: Change k w:将第k条

[BZOJ1984] 月下“毛景树”|树链剖分|线段树

1984: 月下“毛景树” Time Limit: 20 Sec  Memory Limit: 64 MBSubmit: 1088  Solved: 348[Submit][Status][Discuss] Description 毛毛虫经过及时的变形,最终逃过的一劫,离开了菜妈的菜园. 毛毛虫经过千山万水,历尽千辛万苦,最后来到了小小的绍兴一中的校园里.爬啊爬~爬啊爬~~毛毛虫爬到了一颗小小的“毛景树”下面,发现树上长着他最爱吃的毛毛果~~~ “毛景树”上有N个节点和N-1条树枝,但节点上是没

BZOJ 1984: 月下“毛景树”( 树链剖分 )

水水的树链剖分... 将边上的权值转到深度较大的点上 , 然后要注意这样做之后修改或者查询 u , v 转到同一条重链上时 ( 假设 u 深度小 ) , 不能把 u 的权值算上 , 因为是 u 和它的 fa 的边的权值 , 从 u 到 v 并没有经过这条边 线段树维护 3 个域 set , add , max . ---------------------------------------------------------------------------- #include<cstdio

bzoj1984 月下“毛景树”

Description 毛毛虫经过及时的变形,最终逃过的一劫,离开了菜妈的菜园. 毛毛虫经过千山万水,历尽千辛万苦,最后来到了小小的绍兴一中的校园里.爬啊爬~爬啊爬~~毛毛虫爬到了一颗小小的“毛景树”下面,发现树上长着他最爱吃的毛毛果~~~ “毛景树”上有N个节点和N-1条树枝,但节点上是没有毛毛果的,毛毛果都是长在树枝上的.但是这棵“毛景树”有着神奇的魔力,他能改变树枝上毛毛果的个数: ? Change k w:将第k条树枝上毛毛果的个数改变为w个. ? Cover u v w:将节点u与节点

[BZOJ1984]月下“毛景树”解题报告|树链剖分

Description 毛毛虫经过及时的变形,最终逃过的一劫,离开了菜妈的菜园. 毛毛虫经过千山万水,历尽千辛万苦,最后来到了小小的绍兴一中的校园里.爬啊爬~爬啊爬~~毛毛虫爬到了一颗小小的“毛景树”下面,发现树上长着他最爱吃的毛毛果~~~ “毛景树”上有N个节点和N-1条树枝,但节点上是没有毛毛果的,毛毛果都是长在树枝上的.但是这棵“毛景树”有着神奇的魔力,他能改变树枝上毛毛果的个数: ? Change k w:将第k条树枝上毛毛果的个数改变为w个. ? Cover u v w:将节点u与节点

题解 P4315 【月下“毛景树”】

题目链接:Luogu P4315 线段树 \(+\) 树链剖分 \[\Large\texttt{description}\] 给定一棵\(n\)个节点的树,有\(n - 1\)条边相连,给出\(u_i~v_i~w_i\) 分别表示 \(u_i,v_i\)有一条边,边权是\(w_i\) 有\(3\)种操作以及\(1\)种询问 \(\bullet\) Change k w:将第k条树枝上毛毛果的个数改变为w个 \(\bullet\) Cover u v w:将节点u与节点v之间的树枝上毛毛果的个数都

【BZOJ】1984 月下“毛景树”

[算法]树链剖分+线段树 [题解]线段树的区间加值和区间覆盖操作不能同时存在,只能存在一个. 修改:从根节点跑到目标区域路上的标记全部下传,打完标记再上传回根节点(有变动才需要上传). 询问:访问到目标区域路上的标记全部下传. 我写的线段树版本是在打标记的同时便对该点的询问项(最大值)做了对应更改,即可保证访问到该点得到的ms就是该点的答案. 访问某点时如果要询问最大值就直接拿走,如果要还要访问该点的子节点就需要下传. 而修改了某点的值,它的祖先的值就都需要变动,所以一旦修改必须上传至顶. 对于