3999. [TJOI2015]旅游树链剖分

Posted Refun

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了3999. [TJOI2015]旅游树链剖分相关的知识,希望对你有一定的参考价值。

Description

为了提高智商,ZJY准备去往一个新世界去旅游。这个世界的城市布局像一棵树。每两座城市之间只有一条路径可
以互达。每座城市都有一种宝石,有一定的价格。ZJY为了赚取最高利益,她会选择从A城市买入再转手卖到B城市
。由于ZJY买宝石时经常卖萌,因而凡是ZJY路过的城市,这座城市的宝石价格会上涨。让我们来算算ZJY旅游完之
后能够赚取的最大利润。(如a城市宝石价格为v,则ZJY出售价格也为v)

Input

第一行输入一个正整数N,表示城市个数。
接下来一行输入N个正整数表示每座城市宝石的最初价格p,每个宝石的初始价格不超过100。
第三行开始连续输入N-1行,每行有两个数字x和y。表示x城市和y城市有一条路径。城市编号从1开始。
下一行输入一个整数Q,表示询问次数。
接下来Q行,每行输入三个正整数a,b,v,表示ZJY从a旅游到b,城市宝石上涨v。
1≤ N≤50000, 1≤Q ≤50000

Output

 对于每次询问,输出ZJY可能获得的最大利润,如果亏本则输出0。

Sample Input

3
1 2 3
1 2
2 3
2
1 2 100
1 3 100

Sample Output

1
1

Solution

不拍一下还真不知道自己错了不少细节
对拍+debug使我快乐
一开始把题给读错了……
对于这个题,我们要做的就是查询路径上的最大差值(max-min)。
然后就很容易想到用树链剖分维护。
不过这个是有限制的,即在路径上,min必须出现在max前面。
怎么办呢?我们可以分两种情况来考虑。
1、max,min出现在一段完整的重链中。这个我们可以用线段树上的标记很好的维护。
维护一个ans1一个ans2,分别表示不同方向时这一段内的最大差值。(看一下pushup函数就懂了)
2、max,min出现在不同的链中
我们往上跳的时候,按顺序记录一下我们路径经过的轻重链的信息。
跳完之后扫一遍更新答案即可。
细节挺多的反正我拍挂了很多次

Code

  1 #include<iostream>
  2 #include<cstring>
  3 #include<cstdio>
  4 #include<queue>
  5 #define N (50000+100)
  6 using namespace std;
  7 
  8 struct segt{int val,add,max,min,ans1,ans2;}Segt[N<<2],refun[N];
  9 struct node{int to,next;}edge[N<<1];
 10 int n,m,a[N],u,v,l,ans;
 11 int head[N],num_edge;
 12 int Father[N],Depth[N],Son[N],Sum[N];
 13 int T_num[N],Tree[N],Top[N],dfs_num;
 14 queue<segt>q[2];
 15 
 16 void add(int u,int v)
 17 {
 18     edge[++num_edge].to=v;
 19     edge[num_edge].next=head[u];
 20     head[u]=num_edge;
 21 }
 22 
 23 void Dfs1(int x) 
 24 {
 25     Sum[x]=1;
 26     Depth[x]=Depth[Father[x]]+1;
 27     for (int i=head[x]; i; i=edge[i].next)
 28         if (edge[i].to!=Father[x])
 29         {
 30             Father[edge[i].to]=x;
 31             Dfs1(edge[i].to);
 32             Sum[x]+=Sum[edge[i].to];
 33             if (!Son[x] || Sum[Son[x]]<Sum[edge[i].to])
 34                 Son[x]=edge[i].to;
 35         }
 36 }
 37 
 38 void Dfs2(int x,int pre)
 39 {
 40     T_num[x]=++dfs_num;
 41     Tree[dfs_num]=a[x];
 42     Top[x]=pre;
 43     if (Son[x]) Dfs2(Son[x],pre);
 44     for (int i=head[x]; i; i=edge[i].next)
 45         if (edge[i].to!=Father[x] && edge[i].to!=Son[x])
 46             Dfs2(edge[i].to,edge[i].to);
 47 }
 48 
 49 void Pushdown(int now,int l,int r)
 50 {
 51     if (Segt[now].add)
 52     {
 53         int mid=(l+r)>>1;
 54         Segt[now<<1].add+=Segt[now].add;
 55         Segt[now<<1].val+=Segt[now].add*(mid-l+1);
 56         Segt[now<<1|1].add+=Segt[now].add;
 57         Segt[now<<1|1].val+=Segt[now].add*(r-mid);
 58         Segt[now<<1].max+=Segt[now].add;
 59         Segt[now<<1].min+=Segt[now].add;
 60         Segt[now<<1|1].max+=Segt[now].add;
 61         Segt[now<<1|1].min+=Segt[now].add;//一开始max和min忘记更新*%……%&* 
 62         Segt[now].add=0;
 63     }
 64 }
 65 
 66 segt Pushup(int x,segt ls,segt rs)//pushup返回类型改一下方便处理 
 67 {
 68     segt now=Segt[x];
 69     now.val=ls.val+rs.val;
 70     now.max=max(ls.max,rs.max);
 71     now.min=min(ls.min,rs.min);
 72     now.ans1=max(ls.ans1,rs.ans1);
 73     now.ans1=max(now.ans1,rs.max-ls.min);
 74     now.ans2=max(ls.ans2,rs.ans2);
 75     now.ans2=max(now.ans2,ls.max-rs.min);
 76     return now;
 77 }
 78 
 79 void Build(int now,int l,int r)
 80 {
 81     if (l==r){Segt[now].val=Segt[now].max=Segt[now].min=Tree[l];return;}
 82     int mid=(l+r)>>1;
 83     Build(now<<1,l,mid); Build(now<<1|1,mid+1,r);
 84     Segt[now]=Pushup(now,Segt[now<<1],Segt[now<<1|1]);
 85 }
 86 
 87 void Update(int now,int l,int r,int l1,int r1,int k) 
 88 {
 89     if (l>r1 || r<l1) return;
 90     if (l1<=l && r<=r1)
 91     {
 92         Segt[now].val+=(r-l+1)*k;
 93         Segt[now].add+=k;
 94         Segt[now].max+=k;
 95         Segt[now].min+=k;
 96         return;
 97     }
 98     Pushdown(now,l,r);
 99     int mid=(l+r)>>1;
100     Update(now<<1,l,mid,l1,r1,k);Update(now<<1|1,mid+1,r,l1,r1,k);
101     Segt[now]=Pushup(now,Segt[now<<1],Segt[now<<1|1]);
102 }
103 
104 segt Query(int now,int l,int r,int l1,int r1)
105 {
106     if (l1<=l && r<=r1)
107         return Segt[now];
108     Pushdown(now,l,r);
109     int mid=(l+r)>>1;
110     if (r1<=mid) return Query(now<<1,l,mid,l1,r1);
111     if (l1>=mid+1) return Query(now<<1|1,mid+1,r,l1,r1);
112     return Pushup(now,Query(now<<1,l,mid,l1,r1),Query(now<<1|1,mid+1,r,l1,r1));
113 }
114 
115 void Change(int x,int y,int k)
116 {
117     int fx=Top[x],fy=Top[y];
118     while (fx!=fy)
119     {
120         if (Depth[fx]<Depth[fy])
121             swap(x,y),swap(fx,fy);
122         Update(1,1,n,T_num[fx],T_num[x],k);
123         x=Father[fx],fx=Top[x];
124     }
125     if (Depth[x]<Depth[y]) swap(x,y);
126     Update(1,1,n,T_num[y],T_num[x],k);
127 }
128 
129 int Ask(int x,int y)//主要就是这个函数里的问题,我开了两个queue来记录路径。 
130 {
131     int fx=Top[x],fy=Top[y];
132     int now=0,ans=0,cnt=0,h=0,sum;
133     while (fx!=fy)
134     {
135         if (Depth[fx]<Depth[fy])
136             swap(x,y),swap(fx,fy),now^=1;
137         segt no=Query(1,1,n,T_num[fx],T_num[x]);
138         q[now].push(no); cnt++;
139         ans=max(ans,now==1?no.ans1:no.ans2);
140         x=Father[fx],fx=Top[x];
141     }
142     if (Depth[x]<Depth[y]) swap(x,y),now^=1;
143     segt no=Query(1,1,n,T_num[y],T_num[x]);
144     ans=max(ans,now==1?no.ans1:no.ans2);
145     q[now].push(no); cnt++;
146     sum=cnt;
147     
148     while (!q[0].empty()) refun[++h]=q[0].front(),q[0].pop();
149     while (!q[1].empty()) refun[cnt--]=q[1].front(),q[1].pop();
150     int minn=refun[1].min;
151     for (int i=2; i<=sum; ++i)//扫一遍路径上的链,然后更新一下。 
152     {
153         ans=max(ans,refun[i].max-minn);
154         minn=min(minn,refun[i].min);//一开始这里忘了更新了#¥%#¥*& 
155     }
156     return ans;
157 }
158 
159 int main()
160 {
161     scanf("%d",&n);
162     for (int i=1; i<=n; ++i)
163         scanf("%d",&a[i]);
164     for (int i=1; i<=n-1; ++i)
165     {
166         scanf("%d%d",&u,&v);
167         add(u,v); add(v,u);
168     }
169     Dfs1(1); Dfs2(1,1); 
170     Build(1,1,n);
171     scanf("%d",&m);
172     for (int i=1; i<=m; ++i)
173     {
174         scanf("%d%d%d",&u,&v,&l);
175         int ans=Ask(u,v);
176         printf("%d\\n",ans>0?ans:0);
177         Change(u,v,l);
178     }
179 }

以上是关于3999. [TJOI2015]旅游树链剖分的主要内容,如果未能解决你的问题,请参考以下文章

P4592 [TJOI2018]异或 树链剖分 01trie

BZOJ2157: 旅游 树链剖分 线段树

BZOJ 2157 旅游(树链剖分+线段树)

bzoj 2157: 旅游树链剖分+线段树

BZOJ2175旅游(树链剖分,Link-Cut Tree)

bzoj 树链剖分题目汇总