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
1 2 3
1 2
2 3
2
1 2 100
1 3 100
Sample Output
1
1
1
Solution
不拍一下还真不知道自己错了不少细节
对拍+debug使我快乐
一开始把题给读错了……
对拍+debug使我快乐
一开始把题给读错了……
对于这个题,我们要做的就是查询路径上的最大差值(max-min)。
然后就很容易想到用树链剖分维护。
不过这个是有限制的,即在路径上,min必须出现在max前面。
怎么办呢?我们可以分两种情况来考虑。
1、max,min出现在一段完整的重链中。这个我们可以用线段树上的标记很好的维护。
维护一个ans1一个ans2,分别表示不同方向时这一段内的最大差值。(看一下pushup函数就懂了)
2、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